Cppcheck
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
executionpath.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2015 Daniel Marjamäki and 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 #include "executionpath.h"
21 #include "token.h"
22 #include "symboldatabase.h"
23 #include <memory>
24 #include <set>
25 #include <iterator>
26 #include <iostream>
27 
28 
29 
30 // default : bail out if the condition is has variable handling
31 bool ExecutionPath::parseCondition(const Token &tok, std::list<ExecutionPath *> & checks)
32 {
33  unsigned int parlevel = 0;
34  for (const Token *tok2 = &tok; tok2; tok2 = tok2->next()) {
35  if (tok2->str() == "(")
36  ++parlevel;
37  else if (tok2->str() == ")") {
38  if (parlevel == 0)
39  break;
40  --parlevel;
41  } else if (Token::Match(tok2, "[;{}]"))
42  break;
43  if (tok2->varId() != 0) {
44  bailOutVar(checks, tok2->varId());
45  }
46  }
47 
48  for (std::list<ExecutionPath *>::iterator it = checks.begin(); it != checks.end();) {
49  if ((*it)->varId > 0 && (*it)->numberOfIf >= 1) {
50  delete *it;
51  checks.erase(it++);
52  } else {
53  ++it;
54  }
55  }
56 
57 
58  return false;
59 }
60 
61 
63 {
64  std::cout << " varId=" << varId
65  << " numberOfIf=" << numberOfIf
66  << "\n";
67 }
68 
69 // I use this function when debugging ExecutionPaths with GDB
70 /*
71 static void printchecks(const std::list<ExecutionPath *> &checks)
72 {
73  for (std::list<ExecutionPath *>::const_iterator it = checks.begin(); it != checks.end(); ++it)
74  (*it)->print();
75 }
76 */
77 
78 
79 
80 /**
81  * @brief Parse If/Switch body recursively.
82  * @param tok First token in body.
83  * @param checks The current checks
84  * @param newchecks new checks
85  * @param countif The countif set - count number of if for each execution path
86  */
87 static void parseIfSwitchBody(const Token * const tok,
88  const std::list<ExecutionPath *> &checks,
89  std::list<ExecutionPath *> &newchecks,
90  std::set<unsigned int> &countif)
91 {
92  std::set<unsigned int> countif2;
93  std::list<ExecutionPath *> c;
94  if (!checks.empty()) {
95  std::list<ExecutionPath *>::const_iterator it;
96  for (it = checks.begin(); it != checks.end(); ++it) {
97  if ((*it)->numberOfIf == 0)
98  c.push_back((*it)->copy());
99  if ((*it)->varId != 0)
100  countif2.insert((*it)->varId);
101  }
102  }
104  while (!c.empty()) {
105  if (c.back()->varId == 0) {
106  delete c.back();
107  c.pop_back();
108  continue;
109  }
110 
111  bool duplicate = false;
112  std::list<ExecutionPath *>::const_iterator it;
113  for (it = checks.begin(); it != checks.end(); ++it) {
114  if (*(*it) == *c.back() && (*it)->numberOfIf == c.back()->numberOfIf) {
115  duplicate = true;
116  countif2.erase((*it)->varId);
117  break;
118  }
119  }
120  if (!duplicate)
121  newchecks.push_back(c.back());
122  else
123  delete c.back();
124  c.pop_back();
125  }
126 
127  // Add countif2 ids to countif.. countif.
128  countif.insert(countif2.begin(), countif2.end());
129 }
130 
131 
132 void ExecutionPath::checkScope(const Token *tok, std::list<ExecutionPath *> &checks)
133 {
134  if (!tok || tok->str() == "}" || checks.empty())
135  return;
136 
137  const std::auto_ptr<ExecutionPath> check(checks.front()->copy());
138 
139  for (; tok; tok = tok->next()) {
140  // might be a noreturn function..
141  if (Token::simpleMatch(tok->tokAt(-2), ") ; }") &&
142  Token::Match(tok->linkAt(-2)->tokAt(-2), "[;{}] %name% (") &&
143  tok->linkAt(-2)->previous()->varId() == 0) {
144  ExecutionPath::bailOut(checks);
145  return;
146  }
147 
148  // ({ is not handled well
149  if (Token::simpleMatch(tok, "( {")) {
150  ExecutionPath::bailOut(checks);
151  return;
152  }
153 
154  if (Token::simpleMatch(tok, "union {")) {
155  tok = tok->next()->link();
156  continue;
157  }
158 
159  if (tok->str() == "}")
160  return;
161 
162  if (tok->str() == "break") {
163  ExecutionPath::bailOut(checks);
164  return;
165  }
166 
167  if (Token::simpleMatch(tok, "while (")) {
168  // parse condition
169  if (checks.size() > 10 || check->parseCondition(*tok->tokAt(2), checks)) {
170  ExecutionPath::bailOut(checks);
171  return;
172  }
173 
174  // skip "while (fgets()!=NULL)"
175  if (Token::simpleMatch(tok, "while ( fgets (")) {
176  const Token *tok2 = tok->linkAt(3);
177  if (Token::simpleMatch(tok2, ") ) {")) {
178  tok = tok2->linkAt(2);
179  if (!tok)
180  break;
181  continue;
182  }
183  }
184  }
185 
186  // goto/setjmp/longjmp => bailout
187  else if (Token::Match(tok, "goto|setjmp|longjmp")) {
188  ExecutionPath::bailOut(checks);
189  return;
190  }
191 
192  // ?: => bailout
193  if (tok->str() == "?") {
194  for (const Token *tok2 = tok; tok2 && tok2->str() != ";"; tok2 = tok2->next()) {
195  if (tok2->varId() > 0)
196  ExecutionPath::bailOutVar(checks, tok2->varId());
197  }
198  }
199 
200  // for/while/switch/do .. bail out
201  else if (Token::Match(tok, "for|while|switch|do")) {
202  // goto {
203  const Token *tok2 = tok->next();
204  if (tok2 && tok2->str() == "(")
205  tok2 = tok2->link();
206  if (tok2 && tok2->str() == ")")
207  tok2 = tok2->next();
208  if (!tok2 || tok2->str() != "{") {
209  ExecutionPath::bailOut(checks);
210  return;
211  }
212 
213  if (tok->str() == "switch") {
214  // parse condition
215  if (checks.size() > 10 || check->parseCondition(*tok->next(), checks)) {
216  ExecutionPath::bailOut(checks);
217  return;
218  }
219 
220  // what variable ids should the if be counted for?
221  std::set<unsigned int> countif;
222 
223  std::list<ExecutionPath *> newchecks;
224 
225  for (const Token* tok3 = tok2->next(); tok3; tok3 = tok3->next()) {
226  if (tok3->str() == "{")
227  tok3 = tok3->link();
228  else if (tok3->str() == "}")
229  break;
230  else if (tok3->str() == "case" &&
231  !Token::Match(tok3, "case %num% : ; case")) {
232  parseIfSwitchBody(tok3, checks, newchecks, countif);
233  }
234  }
235 
236  // Add newchecks to checks..
237  std::copy(newchecks.begin(), newchecks.end(), std::back_inserter(checks));
238 
239  // Increase numberOfIf
240  std::list<ExecutionPath *>::iterator it;
241  for (it = checks.begin(); it != checks.end(); ++it) {
242  if (countif.find((*it)->varId) != countif.end())
243  (*it)->numberOfIf++;
244  }
245  }
246  // no switch
247  else {
248  for (const Token *tok3 = tok; tok3 && tok3 != tok2; tok3 = tok3->next()) {
249  if (tok3->varId())
250  ExecutionPath::bailOutVar(checks, tok3->varId());
251  }
252 
253  // it is not certain that a for/while will be executed:
254  for (std::list<ExecutionPath *>::iterator it = checks.begin(); it != checks.end();) {
255  if ((*it)->numberOfIf > 0) {
256  delete *it;
257  checks.erase(it++);
258  } else
259  ++it;
260  }
261 
262  // #2231 - loop body only contains a conditional initialization..
263  if (Token::simpleMatch(tok2->next(), "if (")) {
264  // Start { for the if block
265  const Token *tok3 = tok2->linkAt(2);
266  if (Token::simpleMatch(tok3,") {")) {
267  tok3 = tok3->next();
268 
269  // End } for the if block
270  const Token *tok4 = tok3->link();
271  if (Token::Match(tok3, "{ %name% =") &&
272  Token::simpleMatch(tok4, "} }") &&
273  Token::simpleMatch(tok4->tokAt(-2), "break ;")) {
274  // Is there a assignment and then a break?
275  const Token *t = Token::findsimplematch(tok3, ";");
276  if (t && t->tokAt(3) == tok4) {
277  for (std::list<ExecutionPath *>::iterator it = checks.begin(); it != checks.end(); ++it) {
278  if ((*it)->varId == tok3->next()->varId()) {
279  (*it)->numberOfIf++;
280  break;
281  }
282  }
283  tok = tok2->link();
284  continue;
285  }
286  }
287  }
288  }
289 
290  // parse loop bodies
291  check->parseLoopBody(tok2->next(), checks);
292  }
293 
294  // skip { .. }
295  tok2 = tok2->link();
296 
297  // if "do { .. } while ( .." , goto end of while..
298  if (Token::simpleMatch(tok, "do {") && Token::simpleMatch(tok2, "} while ("))
299  tok2 = tok2->linkAt(2);
300 
301  // bail out all variables if the scope contains a "return"
302  // bail out all variables used in this for/while/switch/do
303  for (; tok && tok != tok2; tok = tok->next()) {
304  if (tok->str() == "return")
305  ExecutionPath::bailOut(checks);
306  if (tok->varId())
307  ExecutionPath::bailOutVar(checks, tok->varId());
308  }
309 
310  continue;
311  }
312 
313  // bailout used variables in '; FOREACH ( .. ) { .. }'
314  else if (tok->str() != "if" && Token::Match(tok->previous(), "[;{}] %name% (")) {
315  // goto {
316  const Token *tok2 = tok->next()->link()->next();
317  if (tok2 && tok2->str() == "{") {
318  // goto "}"
319  tok2 = tok2->link();
320 
321  // bail out all variables used in "{ .. }"
322  for (; tok && tok != tok2; tok = tok->next()) {
323  if (tok->varId())
324  ExecutionPath::bailOutVar(checks, tok->varId());
325  }
326  if (!tok)
327  break;
328  }
329  }
330 
331  // .. ) { ... } => bail out
332  if (tok->str() == ")" && tok->next() && tok->next()->str() == "{") {
333  ExecutionPath::bailOut(checks);
334  return;
335  }
336 
337  if ((tok->str() == "abort" || tok->str() == "exit") &&
338  tok->next() && tok->next()->str() == "(") {
339  ExecutionPath::bailOut(checks);
340  return;
341  }
342 
343  // don't parse into "struct type { .."
344  if (Token::Match(tok, "struct|union|class %type% {|:")) {
345  while (tok && tok->str() != "{" && tok->str() != ";")
346  tok = tok->next();
347  tok = tok ? tok->link() : 0;
348  if (!tok) {
349  ExecutionPath::bailOut(checks);
350  return;
351  }
352  }
353 
354  if (Token::simpleMatch(tok, "= {")) {
355  // GCC struct initialization.. bail out
356  if (Token::Match(tok->tokAt(2), ". %name% =")) {
357  ExecutionPath::bailOut(checks);
358  return;
359  }
360 
361  const Token * const end = tok->next()->link();
362  while (tok && tok != end) {
363  if (Token::Match(tok, "[{,] & %var% [,}]"))
364  ExecutionPath::bailOutVar(checks, tok->tokAt(2)->varId());
365  tok = tok->next();
366  }
367  if (!tok) {
368  ExecutionPath::bailOut(checks);
369  return;
370  }
371  continue;
372  }
373 
374  // ; { ... }
375  if (Token::Match(tok->previous(), "[;{}:] {")) {
376  ExecutionPath::checkScope(tok->next(), checks);
377  tok = tok->link();
378  continue;
379  }
380 
381  if (tok->str() == "if" && tok->next() && tok->next()->str() == "(") {
382  // what variable ids should the numberOfIf be counted for?
383  std::set<unsigned int> countif;
384 
385  std::list<ExecutionPath *> newchecks;
386  while (tok->str() == "if" && tok->next() && tok->next()->str() == "(") {
387  // goto "("
388  tok = tok->next();
389 
390  // parse condition
391  if (checks.size() > 10 || check->parseCondition(*tok->next(), checks)) {
392  ExecutionPath::bailOut(checks);
393  ExecutionPath::bailOut(newchecks);
394  return;
395  }
396 
397  // goto ")"
398  tok = tok->link();
399 
400  // goto "{"
401  tok = tok->next();
402 
403  if (!tok || tok->str() != "{") {
404  ExecutionPath::bailOut(checks);
405  ExecutionPath::bailOut(newchecks);
406  return;
407  }
408 
409  // Recursively check into the if ..
410  parseIfSwitchBody(tok->next(), checks, newchecks, countif);
411 
412  // goto "}"
413  tok = tok->link();
414 
415  // there is no else => break out
416  if (!tok->next() || tok->next()->str() != "else")
417  break;
418 
419  // parse next "if"..
420  tok = tok->tokAt(2);
421  if (!tok) {
422  ExecutionPath::bailOut(newchecks);
423  return;
424  }
425  if (tok->str() == "if")
426  continue;
427 
428  // there is no "if"..
429  ExecutionPath::checkScope(tok->next(), checks);
430  tok = tok->link();
431  if (!tok) {
432  ExecutionPath::bailOut(newchecks);
433  return;
434  }
435  }
436 
437  // Add newchecks to checks..
438  std::copy(newchecks.begin(), newchecks.end(), std::back_inserter(checks));
439 
440  // Increase numberOfIf
441  std::list<ExecutionPath *>::iterator it;
442  for (it = checks.begin(); it != checks.end(); ++it) {
443  if (countif.find((*it)->varId) != countif.end())
444  (*it)->numberOfIf++;
445  }
446 
447  // Delete checks that have numberOfIf >= 2
448  for (it = checks.begin(); it != checks.end();) {
449  if ((*it)->varId > 0 && (*it)->numberOfIf >= 2) {
450  delete *it;
451  checks.erase(it++);
452  } else {
453  ++it;
454  }
455  }
456  }
457 
458  tok = check->parse(*tok, checks);
459  if (!tok || checks.empty())
460  return;
461 
462  // return/throw ends all execution paths
463  if (tok->str() == "return" ||
464  tok->str() == "throw" ||
465  tok->str() == "continue" ||
466  tok->str() == "break") {
467  ExecutionPath::bailOut(checks);
468  }
469  }
470 }
471 
472 void checkExecutionPaths(const SymbolDatabase *symbolDatabase, ExecutionPath *c)
473 {
474  for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) {
475  if (i->type != Scope::eFunction || !i->classStart)
476  continue;
477 
478  // Check function
479  std::list<ExecutionPath *> checks;
480  checks.push_back(c->copy());
481  ExecutionPath::checkScope(i->classStart, checks);
482 
483  c->end(checks, i->classEnd);
484 
485  // Cleanup
486  while (!checks.empty()) {
487  delete checks.back();
488  checks.pop_back();
489  }
490  }
491 }