Cppcheck
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
preprocessor.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2016 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 "preprocessor.h"
21 #include "path.h"
22 #include "errorlogger.h"
23 #include "settings.h"
24 #include "simplecpp.h"
25 
26 #include <algorithm>
27 #include <functional>
28 #include <sstream>
29 #include <cstdlib>
30 #include <cctype>
31 #include <vector>
32 #include <set>
33 #include <cstdint>
34 
35 /**
36  * Remove heading and trailing whitespaces from the input parameter.
37  * If string is all spaces/tabs, return empty string.
38  * @param s The string to trim.
39  */
40 static std::string trim(const std::string& s)
41 {
42  const std::string::size_type beg = s.find_first_not_of(" \t");
43  if (beg == std::string::npos)
44  return "";
45  const std::string::size_type end = s.find_last_not_of(" \t");
46  return s.substr(beg, end - beg + 1);
47 }
48 
49 Directive::Directive(const std::string &_file, const int _linenr, const std::string &_str):
50  file(_file),
51  linenr(_linenr),
52  str(_str)
53 {
54  str = trim(str);
55 }
56 
59 
60 char Preprocessor::macroChar = char(1);
61 
62 Preprocessor::Preprocessor(Settings& settings, ErrorLogger *errorLogger) : _settings(settings), _errorLogger(errorLogger)
63 {
64 }
65 
67 {
68  for (std::map<std::string, simplecpp::TokenList *>::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it)
69  delete it->second;
70 }
71 
72 
73 static void inlineSuppressions(const simplecpp::TokenList &tokens, Settings &_settings)
74 {
75  std::list<std::string> suppressionIDs;
76 
77  for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
78  if (tok->comment) {
79  std::istringstream iss(tok->str.substr(2));
80  std::string word;
81  iss >> word;
82  if (word != "cppcheck-suppress")
83  continue;
84  iss >> word;
85  if (iss)
86  suppressionIDs.push_back(word);
87  continue;
88  }
89 
90  if (suppressionIDs.empty())
91  continue;
92 
93  // Relative filename
94  std::string relativeFilename(tok->location.file());
95  if (_settings.relativePaths) {
96  for (std::size_t j = 0U; j < _settings.basePaths.size(); ++j) {
97  const std::string bp = _settings.basePaths[j] + "/";
98  if (relativeFilename.compare(0,bp.size(),bp)==0) {
99  relativeFilename = relativeFilename.substr(bp.size());
100  }
101  }
102  }
103 
104  // Add the suppressions.
105  for (std::list<std::string>::const_iterator it = suppressionIDs.begin(); it != suppressionIDs.end(); ++it) {
106  _settings.nomsg.addSuppression(*it, relativeFilename, tok->location.line);
107  }
108  suppressionIDs.clear();
109  }
110 }
111 
112 void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens)
113 {
115  return;
117  for (std::map<std::string,simplecpp::TokenList*>::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
118  if (it->second)
119  ::inlineSuppressions(*it->second, _settings);
120  }
121 }
122 
123 void Preprocessor::setDirectives(const simplecpp::TokenList &tokens1)
124 {
125  // directive list..
126  directives.clear();
127 
128  std::list<const simplecpp::TokenList *> list;
129  list.push_back(&tokens1);
130  for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
131  list.push_back(it->second);
132  }
133 
134  for (std::list<const simplecpp::TokenList *>::const_iterator it = list.begin(); it != list.end(); ++it) {
135  for (const simplecpp::Token *tok = (*it)->cfront(); tok; tok = tok ? tok->next : nullptr) {
136  if ((tok->op != '#') || (tok->previous && tok->previous->location.line == tok->location.line))
137  continue;
138  if (tok->next && tok->next->str == "endfile")
139  continue;
140  Directive directive(tok->location.file(), tok->location.line, "");
141  for (const simplecpp::Token *tok2 = tok; tok2 && tok2->location.line == directive.linenr; tok2 = tok2->next) {
142  if (tok2->comment)
143  continue;
144  if (!directive.str.empty() && (tok2->location.col > tok2->previous->location.col + tok2->previous->str.size()))
145  directive.str += ' ';
146  if (directive.str == "#" && tok2->str == "file")
147  directive.str += "include";
148  else
149  directive.str += tok2->str;
150  }
151  directives.push_back(directive);
152  }
153  }
154 }
155 
156 static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
157 {
158  return tok1 && tok2 && tok1->location.sameline(tok2->location);
159 }
160 
161 static std::string readcondition(const simplecpp::Token *iftok, const std::set<std::string> &defined, const std::set<std::string> &undefined)
162 {
163  const simplecpp::Token *cond = iftok->next;
164  if (!sameline(iftok,cond))
165  return "";
166 
167  const simplecpp::Token *next1 = cond->next;
168  const simplecpp::Token *next2 = next1 ? next1->next : nullptr;
169  const simplecpp::Token *next3 = next2 ? next2->next : nullptr;
170 
171  unsigned int len = 1;
172  if (sameline(iftok,next1))
173  len = 2;
174  if (sameline(iftok,next2))
175  len = 3;
176  if (sameline(iftok,next3))
177  len = 4;
178 
179  if (len == 1 && cond->str == "0")
180  return "0";
181 
182  if (len == 1 && cond->name) {
183  if (defined.find(cond->str) == defined.end())
184  return cond->str;
185  }
186 
187  if (len == 3 && cond->op == '(' && next1->name && next2->op == ')') {
188  if (defined.find(next1->str) == defined.end() && undefined.find(next1->str) == undefined.end())
189  return next1->str;
190  }
191 
192  if (len == 3 && cond->name && next1->str == "==" && next2->number) {
193  if (defined.find(cond->str) == defined.end())
194  return cond->str + '=' + cond->next->next->str;
195  }
196 
197  std::set<std::string> configset;
198  for (; sameline(iftok,cond); cond = cond->next) {
199  if (cond->op == '!')
200  break;
201  if (cond->str != "defined")
202  continue;
203  const simplecpp::Token *dtok = cond->next;
204  if (!dtok)
205  break;
206  if (dtok->op == '(')
207  dtok = dtok->next;
208  if (sameline(iftok,dtok) && dtok->name && defined.find(dtok->str) == defined.end() && undefined.find(dtok->str) == undefined.end())
209  configset.insert(dtok->str);
210  }
211  std::string cfg;
212  for (std::set<std::string>::const_iterator it = configset.begin(); it != configset.end(); ++it) {
213  if (!cfg.empty())
214  cfg += ';';
215  cfg += *it;
216  }
217  return cfg;
218 }
219 
220 static bool hasDefine(const std::string &userDefines, const std::string &cfg)
221 {
222  if (cfg.empty()) {
223  return false;
224  }
225 
226  std::string::size_type pos = 0;
227  while (pos < userDefines.size()) {
228  pos = userDefines.find(cfg, pos);
229  if (pos == std::string::npos)
230  break;
231  const std::string::size_type pos2 = pos + cfg.size();
232  if ((pos == 0 || userDefines[pos-1U] == ';') && (pos2 == userDefines.size() || userDefines[pos2] == '='))
233  return true;
234  pos = pos2;
235  }
236  return false;
237 }
238 
239 static std::string cfg(const std::vector<std::string> &configs, const std::string &userDefines)
240 {
241  std::set<std::string> configs2(configs.begin(), configs.end());
242  std::string ret;
243  for (std::set<std::string>::const_iterator it = configs2.begin(); it != configs2.end(); ++it) {
244  if (it->empty())
245  continue;
246  if (*it == "0")
247  return "";
248  if (hasDefine(userDefines, *it))
249  continue;
250  if (!ret.empty())
251  ret += ';';
252  ret += *it;
253  }
254  return ret;
255 }
256 
257 static bool isUndefined(const std::string &cfg, const std::set<std::string> &undefined)
258 {
259  for (std::string::size_type pos1 = 0U; pos1 < cfg.size();) {
260  const std::string::size_type pos2 = cfg.find(';',pos1);
261  const std::string def = (pos2 == std::string::npos) ? cfg.substr(pos1) : cfg.substr(pos1, pos2 - pos1);
262 
263  std::string::size_type eq = def.find('=');
264  if (eq == std::string::npos && undefined.find(def) != undefined.end())
265  return true;
266  if (eq != std::string::npos && undefined.find(def.substr(0,eq)) != undefined.end() && def.substr(eq) != "=0")
267  return true;
268 
269  pos1 = (pos2 == std::string::npos) ? pos2 : pos2 + 1U;
270  }
271  return false;
272 }
273 
274 static bool getConfigsElseIsFalse(const std::vector<std::string> &configs_if, const std::string &userDefines)
275 {
276  for (unsigned int i = 0; i < configs_if.size(); ++i) {
277  if (hasDefine(userDefines, configs_if[i]))
278  return true;
279  }
280  return false;
281 }
282 
283 static const simplecpp::Token *gotoEndIf(const simplecpp::Token *cmdtok)
284 {
285  int level = 0;
286  while (nullptr != (cmdtok = cmdtok->next)) {
287  if (cmdtok->op == '#' && !sameline(cmdtok->previous,cmdtok) && sameline(cmdtok, cmdtok->next)) {
288  if (cmdtok->next->str.compare(0,2,"if")==0)
289  ++level;
290  else if (cmdtok->next->str == "endif") {
291  --level;
292  if (level < 0)
293  return cmdtok;
294  }
295  }
296  }
297  return nullptr;
298 }
299 
300 static void getConfigs(const simplecpp::TokenList &tokens, std::set<std::string> &defined, const std::string &userDefines, const std::set<std::string> &undefined, std::set<std::string> &ret)
301 {
302  std::vector<std::string> configs_if;
303  std::vector<std::string> configs_ifndef;
304 
305  for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
306  if (tok->op != '#' || sameline(tok->previous, tok))
307  continue;
308  const simplecpp::Token *cmdtok = tok->next;
309  if (!sameline(tok, cmdtok))
310  continue;
311  if (cmdtok->str == "ifdef" || cmdtok->str == "ifndef" || cmdtok->str == "if") {
312  std::string config;
313  if (cmdtok->str == "ifdef" || cmdtok->str == "ifndef") {
314  const simplecpp::Token *expr1 = cmdtok->next;
315  if (sameline(tok,expr1) && expr1->name && !sameline(tok,expr1->next))
316  config = expr1->str;
317  if (defined.find(config) != defined.end())
318  config.clear();
319  } else if (cmdtok->str == "if") {
320  config = readcondition(cmdtok, defined, undefined);
321  }
322 
323  // skip undefined configurations..
324  if (isUndefined(config, undefined))
325  config.clear();
326 
327  configs_if.push_back((cmdtok->str == "ifndef") ? std::string() : config);
328  configs_ifndef.push_back((cmdtok->str == "ifndef") ? config : std::string());
329  ret.insert(cfg(configs_if,userDefines));
330  } else if (cmdtok->str == "elif" || cmdtok->str == "else") {
331  if (getConfigsElseIsFalse(configs_if,userDefines)) {
332  tok = gotoEndIf(tok);
333  if (!tok)
334  break;
335  tok = tok->previous;
336  continue;
337  }
338  if (!configs_if.empty())
339  configs_if.pop_back();
340  if (cmdtok->str == "elif") {
341  std::string config = readcondition(cmdtok, defined, undefined);
342  if (isUndefined(config,undefined))
343  config.clear();
344  configs_if.push_back(config);
345  ret.insert(cfg(configs_if, userDefines));
346  } else if (!configs_ifndef.empty()) {
347  configs_if.push_back(configs_ifndef.back());
348  ret.insert(cfg(configs_if, userDefines));
349  }
350  } else if (cmdtok->str == "endif" && !sameline(tok, cmdtok->next)) {
351  if (!configs_if.empty())
352  configs_if.pop_back();
353  if (!configs_ifndef.empty())
354  configs_ifndef.pop_back();
355  } else if (cmdtok->str == "define" && sameline(tok, cmdtok->next) && cmdtok->next->name) {
356  defined.insert(cmdtok->next->str);
357  }
358  }
359 }
360 
361 
362 std::set<std::string> Preprocessor::getConfigs(const simplecpp::TokenList &tokens) const
363 {
364  std::set<std::string> ret;
365  ret.insert("");
366  if (!tokens.cfront())
367  return ret;
368 
369  std::set<std::string> defined;
370  defined.insert("__cplusplus");
371 
372  ::getConfigs(tokens, defined, _settings.userDefines, _settings.userUndefs, ret);
373 
374  for (std::map<std::string, simplecpp::TokenList*>::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
375  if (!_settings.configurationExcluded(it->first))
376  ::getConfigs(*(it->second), defined, _settings.userDefines, _settings.userUndefs, ret);
377  }
378 
379  return ret;
380 }
381 
382 
383 void Preprocessor::preprocess(std::istream &istr, std::map<std::string, std::string> &result, const std::string &filename, const std::list<std::string> &includePaths)
384 {
385  (void)includePaths;
386 
387  simplecpp::OutputList outputList;
388  std::vector<std::string> files;
389  const simplecpp::TokenList tokens1(istr, files, filename, &outputList);
390 
391 
392  const std::set<std::string> configs = getConfigs(tokens1);
393 
394  for (std::set<std::string>::const_iterator it = configs.begin(); it != configs.end(); ++it) {
395  if (_settings.userUndefs.find(*it) == _settings.userUndefs.end()) {
396  result[ *it ] = getcode(tokens1, *it, files, false);
397  }
398  }
399 }
400 
401 std::string Preprocessor::removeSpaceNearNL(const std::string &str)
402 {
403  std::string tmp;
404  char prev = '\n'; // treat start of file as newline
405  for (std::size_t i = 0; i < str.size(); i++) {
406  if (str[i] == ' ' &&
407  (prev == '\n' ||
408  i + 1 >= str.size() || // treat end of file as newline
409  str[i+1] == '\n'
410  )
411  ) {
412  // Ignore space that has new line in either side of it
413  } else {
414  tmp.append(1, str[i]);
415  prev = str[i];
416  }
417  }
418 
419  return tmp;
420 }
421 
422 void Preprocessor::preprocessWhitespaces(std::string &processedFile)
423 {
424  // Replace all tabs with spaces..
425  std::replace(processedFile.begin(), processedFile.end(), '\t', ' ');
426 
427  // Remove space characters that are after or before new line character
428  processedFile = removeSpaceNearNL(processedFile);
429 }
430 
431 void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list<std::string> &resultConfigurations, const std::string &filename, const std::list<std::string> &includePaths)
432 {
433  (void)includePaths;
434 
435  if (file0.empty())
436  file0 = filename;
437 
438  simplecpp::OutputList outputList;
439  std::vector<std::string> files;
440  const simplecpp::TokenList tokens1(srcCodeStream, files, filename, &outputList);
441 
442  const std::set<std::string> configs = getConfigs(tokens1);
443  for (std::set<std::string>::const_iterator it = configs.begin(); it != configs.end(); ++it)
444  resultConfigurations.push_back(*it);
445 
446  processedFile = tokens1.stringify();
447 }
448 
449 static void splitcfg(const std::string &cfg, std::list<std::string> &defines, const std::string &defaultValue)
450 {
451  for (std::string::size_type defineStartPos = 0U; defineStartPos < cfg.size();) {
452  const std::string::size_type defineEndPos = cfg.find(';', defineStartPos);
453  std::string def = (defineEndPos == std::string::npos) ? cfg.substr(defineStartPos) : cfg.substr(defineStartPos, defineEndPos - defineStartPos);
454  if (!defaultValue.empty() && def.find('=') == std::string::npos)
455  def += '=' + defaultValue;
456  defines.push_back(def);
457  if (defineEndPos == std::string::npos)
458  break;
459  defineStartPos = defineEndPos + 1U;
460  }
461 }
462 
463 static simplecpp::DUI createDUI(const Settings &_settings, const std::string &cfg, const std::string &filename)
464 {
465  simplecpp::DUI dui;
466 
467  splitcfg(_settings.userDefines, dui.defines, "1");
468  if (!cfg.empty())
469  splitcfg(cfg, dui.defines, "");
470 
471  for (std::vector<std::string>::const_iterator it = _settings.library.defines.begin(); it != _settings.library.defines.end(); ++it) {
472  if (it->compare(0,8,"#define ")!=0)
473  continue;
474  std::string s = it->substr(8);
475  std::string::size_type pos = s.find_first_of(" (");
476  if (pos == std::string::npos) {
477  dui.defines.push_back(s);
478  continue;
479  }
480  if (s[pos] == ' ') {
481  s[pos] = '=';
482  } else {
483  s[s.find(')')+1] = '=';
484  }
485  dui.defines.push_back(s);
486  }
487 
488  if (Path::isCPP(filename))
489  dui.defines.push_back("__cplusplus");
490 
491  dui.undefined = _settings.userUndefs; // -U
492  dui.includePaths = _settings.includePaths; // -I
493  dui.includes = _settings.userIncludes; // --include
494  return dui;
495 }
496 
497 static bool hasErrors(const simplecpp::OutputList &outputList)
498 {
499  for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) {
500  switch (it->type) {
501  case simplecpp::Output::ERROR:
502  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
503  case simplecpp::Output::SYNTAX_ERROR:
504  return true;
505  case simplecpp::Output::WARNING:
506  case simplecpp::Output::MISSING_HEADER:
507  case simplecpp::Output::PORTABILITY_BACKSLASH:
508  break;
509  };
510  }
511  return false;
512 }
513 
514 
515 void Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector<std::string> &files)
516 {
517  const simplecpp::DUI dui = createDUI(_settings, "", files[0]);
518 
519  simplecpp::OutputList outputList;
520 
521  tokenlists = simplecpp::load(rawtokens, files, dui, &outputList);
522 }
523 
525 {
526  for (std::map<std::string, simplecpp::TokenList*>::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
527  if (it->second)
528  it->second->removeComments();
529  }
530 }
531 
532 void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const
533 {
534  tokens->sizeOfType["bool"] = _settings.sizeof_bool;
535  tokens->sizeOfType["short"] = _settings.sizeof_short;
536  tokens->sizeOfType["int"] = _settings.sizeof_int;
537  tokens->sizeOfType["long"] = _settings.sizeof_long;
538  tokens->sizeOfType["long long"] = _settings.sizeof_long_long;
539  tokens->sizeOfType["float"] = _settings.sizeof_float;
540  tokens->sizeOfType["double"] = _settings.sizeof_double;
541  tokens->sizeOfType["long double"] = _settings.sizeof_long_double;
542  tokens->sizeOfType["bool *"] = _settings.sizeof_pointer;
543  tokens->sizeOfType["short *"] = _settings.sizeof_pointer;
544  tokens->sizeOfType["int *"] = _settings.sizeof_pointer;
545  tokens->sizeOfType["long *"] = _settings.sizeof_pointer;
546  tokens->sizeOfType["long long *"] = _settings.sizeof_pointer;
547  tokens->sizeOfType["float *"] = _settings.sizeof_pointer;
548  tokens->sizeOfType["double *"] = _settings.sizeof_pointer;
549  tokens->sizeOfType["long double *"] = _settings.sizeof_pointer;
550 }
551 
552 std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, const bool writeLocations)
553 {
554  const std::string filename(files[0]);
555 
556  const simplecpp::DUI dui = createDUI(_settings, cfg, filename);
557 
558  simplecpp::OutputList outputList;
559  std::list<simplecpp::MacroUsage> macroUsage;
560  simplecpp::TokenList tokens2(files);
561  simplecpp::preprocess(tokens2, tokens1, files, tokenlists, dui, &outputList, &macroUsage);
562 
563  bool showerror = (!_settings.userDefines.empty() && !_settings.force);
564  reportOutput(outputList, showerror);
565  if (hasErrors(outputList))
566  return "";
567 
568  // ensure that guessed define macros without value are not used in the code
569  if (!validateCfg(cfg, macroUsage))
570  return "";
571 
572  // assembler code locations..
573  std::set<simplecpp::Location> assemblerLocations;
574  for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
575  const Directive &d1 = *dirIt;
576  if (d1.str.compare(0, 11, "#pragma asm") != 0)
577  continue;
578  std::list<Directive>::const_iterator dirIt2 = dirIt;
579  ++dirIt2;
580  if (dirIt2 == directives.end())
581  continue;
582 
583  const Directive &d2 = *dirIt2;
584  if (d2.str.compare(0,14,"#pragma endasm") != 0 || d1.file != d2.file)
585  continue;
586 
587  simplecpp::Location loc(files);
588  loc.fileIndex = ~0U;
589  loc.col = 0U;
590  for (unsigned int i = 0; i < files.size(); ++i) {
591  if (files[i] == d1.file) {
592  loc.fileIndex = i;
593  break;
594  }
595  }
596 
597  for (unsigned int linenr = d1.linenr + 1U; linenr < d2.linenr; linenr++) {
598  loc.line = linenr;
599  assemblerLocations.insert(loc);
600  }
601  }
602 
603  unsigned int prevfile = 0;
604  unsigned int line = 1;
605  std::ostringstream ret;
606  for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) {
607  if (writeLocations && tok->location.fileIndex != prevfile) {
608  ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
609  prevfile = tok->location.fileIndex;
610  line = tok->location.line;
611  }
612 
613  if (tok->previous && line == tok->location.line)
614  ret << ' ';
615  bool newline = false;
616  while (tok->location.line > line) {
617  ret << '\n';
618  line++;
619  newline = true;
620  }
621  if (newline) {
622  simplecpp::Location loc = tok->location;
623  loc.col = 0U;
624  if (assemblerLocations.find(loc) != assemblerLocations.end()) {
625  ret << "asm();";
626  while (assemblerLocations.find(loc) != assemblerLocations.end()) {
627  loc.line++;
628  }
629  while (tok && tok->location.line < loc.line)
630  tok = tok->next;
631  if (!tok)
632  break;
633  while (line < tok->location.line) {
634  ret << '\n';
635  ++line;
636  }
637  }
638  }
639  if (!tok->macro.empty())
641  ret << tok->str;
642  }
643 
644  return ret.str();
645 }
646 
647 std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename)
648 {
649  simplecpp::OutputList outputList;
650  std::vector<std::string> files;
651 
652  std::istringstream istr(filedata);
653  simplecpp::TokenList tokens1(istr, files, Path::simplifyPath(filename), &outputList);
654  inlineSuppressions(tokens1);
655  tokens1.removeComments();
656  removeComments();
657  setDirectives(tokens1);
658 
659  reportOutput(outputList, true);
660 
661  if (hasErrors(outputList))
662  return "";
663 
664  return getcode(tokens1, cfg, files, filedata.find("#file") != std::string::npos);
665 }
666 
667 void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror)
668 {
669  for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) {
670  switch (it->type) {
671  case simplecpp::Output::ERROR:
672  if (it->msg.compare(0,6,"#error")!=0 || showerror)
673  error(it->location.file(), it->location.line, it->msg);
674  break;
675  case simplecpp::Output::WARNING:
676  case simplecpp::Output::PORTABILITY_BACKSLASH:
677  break;
678  case simplecpp::Output::MISSING_HEADER: {
679  const std::string::size_type pos1 = it->msg.find_first_of("<\"");
680  const std::string::size_type pos2 = it->msg.find_first_of(">\"", pos1 + 1U);
681  if (pos1 < pos2 && pos2 != std::string::npos)
682  missingInclude(it->location.file(), it->location.line, it->msg.substr(pos1+1, pos2-pos1-1), it->msg[pos1] == '\"' ? UserHeader : SystemHeader);
683  }
684  break;
685  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
686  case simplecpp::Output::SYNTAX_ERROR:
687  error(it->location.file(), it->location.line, it->msg);
688  break;
689  };
690  }
691 }
692 
693 void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg)
694 {
695  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
696  if (!filename.empty()) {
697  ErrorLogger::ErrorMessage::FileLocation loc(filename, linenr);
698  locationList.push_back(loc);
699  }
701  file0,
703  msg,
704  "preprocessorErrorDirective",
705  false));
706 }
707 
708 // Report that include is missing
709 void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
710 {
711  const std::string fname = Path::fromNativeSeparators(filename);
712  if (_settings.nomsg.isSuppressed("missingInclude", fname, linenr))
713  return;
714  if (headerType == SystemHeader && _settings.nomsg.isSuppressed("missingIncludeSystem", fname, linenr))
715  return;
716 
717  if (headerType == SystemHeader)
719  else
720  missingIncludeFlag = true;
722 
723  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
724  if (!filename.empty()) {
726  loc.line = linenr;
727  loc.setfile(Path::toNativeSeparators(filename));
728  locationList.push_back(loc);
729  }
731  (headerType==SystemHeader) ?
732  "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." :
733  "Include file: \"" + header + "\" not found.",
734  (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude",
735  false);
736  _errorLogger->reportInfo(errmsg);
737  }
738 }
739 
740 bool Preprocessor::validateCfg(const std::string &cfg, const std::list<simplecpp::MacroUsage> &macroUsageList)
741 {
742  bool ret = true;
743  std::list<std::string> defines;
744  splitcfg(cfg, defines, std::string());
745  for (std::list<std::string>::const_iterator defineIt = defines.begin(); defineIt != defines.end(); ++defineIt) {
746  if (defineIt->find('=') != std::string::npos)
747  continue;
748  const std::string macroName(defineIt->substr(0, defineIt->find('(')));
749  for (std::list<simplecpp::MacroUsage>::const_iterator usageIt = macroUsageList.begin(); usageIt != macroUsageList.end(); ++usageIt) {
750  const simplecpp::MacroUsage &mu = *usageIt;
751  if (mu.macroName != macroName)
752  continue;
753  bool directiveLocation = false;
754  for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
755  if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) {
756  directiveLocation = true;
757  break;
758  }
759  }
760  if (!directiveLocation) {
761  if (_settings.isEnabled("information"))
762  validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName);
763  ret = false;
764  }
765  }
766  }
767 
768  return ret;
769 }
770 
771 void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro)
772 {
773  const std::string id = "ConfigurationNotChecked";
774  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
776  locationList.push_back(loc);
777  ErrorLogger::ErrorMessage errmsg(locationList, file0, Severity::information, "Skipping configuration '" + cfg + "' since the value of '" + macro + "' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.", id, false);
778  _errorLogger->reportInfo(errmsg);
779 }
780 
781 void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
782 {
783  Settings settings2(*settings);
784  Preprocessor preprocessor(settings2, errorLogger);
785  settings2.checkConfiguration=true;
786  preprocessor.missingInclude("", 1, "", UserHeader);
787  preprocessor.missingInclude("", 1, "", SystemHeader);
788  preprocessor.validateCfgError("", 1, "X", "X");
789  preprocessor.error("", 1, "#error message"); // #error ..
790 }
791 
792 void Preprocessor::dump(std::ostream &out) const
793 {
794  // Create a xml directive dump.
795  // The idea is not that this will be readable for humans. It's a
796  // data dump that 3rd party tools could load and get useful info from.
797  std::list<Directive>::const_iterator it;
798 
799  out << " <directivelist>" << std::endl;
800 
801  for (it = directives.begin(); it != directives.end(); ++it) {
802  out << " <directive "
803  << "file=\"" << it->file << "\" "
804  << "linenr=\"" << it->linenr << "\" "
805  // str might contain characters such as '"', '<' or '>' which
806  // could result in invalid XML, so run it through toxml().
807  << "str=\"" << ErrorLogger::toxml(it->str) << "\"/>" << std::endl;
808  }
809  out << " </directivelist>" << std::endl;
810 }
811 
812 static const std::uint32_t crc32Table[] = {
813  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
814  0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
815  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
816  0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
817  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
818  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
819  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
820  0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
821  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
822  0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
823  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
824  0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
825  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
826  0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
827  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
828  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
829  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
830  0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
831  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
832  0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
833  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
834  0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
835  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
836  0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
837  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
838  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
839  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
840  0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
841  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
842  0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
843  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
844  0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
845  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
846  0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
847  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
848  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
849  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
850  0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
851  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
852  0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
853  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
854  0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
855  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
856 };
857 
858 static std::uint32_t crc32(const std::string &data)
859 {
860  std::uint32_t crc = ~0U;
861  for (std::string::const_iterator c = data.begin(); c != data.end(); ++c) {
862  crc = crc32Table[(crc ^ (unsigned char)(*c)) & 0xFF] ^ (crc >> 8);
863  }
864  return crc ^ ~0U;
865 }
866 
867 unsigned int Preprocessor::calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
868 {
869  std::ostringstream ostr;
870  ostr << toolinfo << '\n';
871  for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
872  if (!tok->comment)
873  ostr << tok->str;
874  }
875  for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
876  for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) {
877  if (!tok->comment)
878  ostr << tok->str;
879  }
880  }
881  return crc32(ostr.str());
882 }
virtual void reportErr(const ErrorLogger::ErrorMessage &msg)=0
Information about found errors and warnings is directed here.
static std::string toxml(const std::string &str)
Convert XML-sensitive characters into XML entities.
Wrapper for error messages, provided by reportErr()
Definition: errorlogger.h:171
bool validateCfg(const std::string &cfg, const std::list< simplecpp::MacroUsage > &macroUsageList)
make sure empty configuration macros are not used in code.
static const simplecpp::Token * gotoEndIf(const simplecpp::Token *cmdtok)
void error(const std::string &filename, unsigned int linenr, const std::string &msg)
void inlineSuppressions(const simplecpp::TokenList &tokens)
unsigned int sizeof_bool
bits in long long
Definition: platform.h:67
std::list< std::string > includePaths
List of include paths, e.g.
Definition: settings.h:150
HeaderTypes
Include file types.
Definition: preprocessor.h:74
std::string str
the actual directive text
Definition: preprocessor.h:54
Programming error.
Definition: errorlogger.h:75
static bool hasDefine(const std::string &userDefines, const std::string &cfg)
static const std::uint32_t crc32Table[]
unsigned int sizeof_long
Definition: platform.h:70
bool isEnabled(T &&str) const
Returns true if given id is in the list of enabled extra checks (–enable)
Definition: settings.h:169
void removeComments()
virtual void reportInfo(const ErrorLogger::ErrorMessage &msg)
Output information messages.
Definition: errorlogger.h:320
bool relativePaths
Use relative paths in output.
Definition: settings.h:116
static std::string fromNativeSeparators(std::string path)
Convert path to use internal path separators.
Definition: path.cpp:58
Checking information.
Definition: errorlogger.h:109
static std::string readcondition(const simplecpp::Token *iftok, const std::set< std::string > &defined, const std::set< std::string > &undefined)
unsigned int sizeof_short
Definition: platform.h:68
Settings & _settings
Definition: preprocessor.h:202
unsigned int linenr
line number in (possibly included) file where directive is defined
Definition: preprocessor.h:51
virtual ~Preprocessor()
The cppcheck preprocessor.
Definition: preprocessor.h:68
unsigned int sizeof_long_long
Definition: platform.h:71
Directive(const std::string &_file, const int _linenr, const std::string &_str)
record a directive (possibly filtering src)
static std::string simplifyPath(std::string originalPath)
Simplify path "foo/bar/.." => "foo".
Definition: path.cpp:66
std::string file
name of (possibly included) file where directive is defined
Definition: preprocessor.h:48
static bool isUndefined(const std::string &cfg, const std::set< std::string > &undefined)
static std::string trim(const std::string &s)
Remove heading and trailing whitespaces from the input parameter.
static std::uint32_t crc32(const std::string &data)
void setDirectives(const simplecpp::TokenList &tokens)
static void splitcfg(const std::string &cfg, std::list< std::string > &defines, const std::string &defaultValue)
static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
std::string addSuppression(const std::string &errorId, const std::string &file=emptyString, unsigned int line=0)
Don't show this error.
unsigned int sizeof_float
Definition: platform.h:72
std::list< Directive > directives
list of all directives met while preprocessing file
Definition: preprocessor.h:206
bool checkConfiguration
Is the 'configuration checking' wanted?
Definition: settings.h:242
static void inlineSuppressions(const simplecpp::TokenList &tokens, Settings &_settings)
This is an interface, which the class responsible of error logging should implement.
Definition: errorlogger.h:165
unsigned int sizeof_pointer
Definition: platform.h:77
bool isSuppressed(const std::string &errorId, const std::string &file, unsigned int line)
Returns true if this message should not be shown to the user.
static void getConfigs(const simplecpp::TokenList &tokens, std::set< std::string > &defined, const std::string &userDefines, const std::set< std::string > &undefined, std::set< std::string > &ret)
Suppressions nomsg
suppress message (–suppressions)
Definition: settings.h:196
std::set< std::string > userUndefs
undefines given by the user
Definition: settings.h:205
static void preprocessWhitespaces(std::string &processedFile)
preprocess all whitespaces
unsigned int sizeof_double
Definition: platform.h:73
std::vector< std::string > defines
Definition: library.h:383
static bool missingSystemIncludeFlag
Definition: preprocessor.h:87
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:45
void preprocess(std::istream &istr, std::map< std::string, std::string > &result, const std::string &filename, const std::list< std::string > &includePaths=std::list< std::string >())
Extract the code for each configuration.
static bool isCPP(const std::string &extensionInLowerCase)
Identify language based on file extension.
Definition: path.cpp:246
std::string getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector< std::string > &files, const bool writeLocations)
static std::string removeSpaceNearNL(const std::string &str)
Remove space that has new line character on left or right side of it.
std::map< std::string, simplecpp::TokenList * > tokenlists
Definition: preprocessor.h:208
Library library
Library (–library)
Definition: settings.h:218
static char macroChar
character that is inserted in expanded macros
Definition: preprocessor.h:81
A preprocessor directive Each preprocessor directive (#include, #define, #undef, #if, #ifdef, #else, #endif) will be recorded as an instance of this class.
Definition: preprocessor.h:45
std::set< std::string > getConfigs(const simplecpp::TokenList &tokens) const
static bool getConfigsElseIsFalse(const std::vector< std::string > &configs_if, const std::string &userDefines)
std::string userDefines
defines given by the user
Definition: settings.h:202
Preprocessor(Settings &settings, ErrorLogger *errorLogger=nullptr)
void reportOutput(const simplecpp::OutputList &outputList, bool showerror)
unsigned int calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
Calculate CRC32 checksum.
void dump(std::ostream &out) const
dump all directives present in source file
unsigned int sizeof_int
Definition: platform.h:69
void setPlatformInfo(simplecpp::TokenList *tokens) const
ErrorLogger * _errorLogger
Definition: preprocessor.h:203
bool configurationExcluded(const std::string &file) const
return true if a included file is to be excluded in Preprocessor::getConfigs
Definition: settings.h:256
static bool hasErrors(const simplecpp::OutputList &outputList)
static void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
File name and line number.
Definition: errorlogger.h:178
unsigned int sizeof_long_double
Definition: platform.h:74
bool inlineSuppressions
Is –inline-suppr given?
Definition: settings.h:97
static std::string cfg(const std::vector< std::string > &configs, const std::string &userDefines)
void missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
std::string file0
filename for cpp/c file - useful when reporting errors
Definition: preprocessor.h:211
void loadFiles(const simplecpp::TokenList &rawtokens, std::vector< std::string > &files)
bool force
Force checking the files with "too many" configurations (–force).
Definition: settings.h:113
std::vector< std::string > basePaths
Paths used as base for conversion to relative paths.
Definition: settings.h:119
void setfile(const std::string &file)
Set the filename.
std::list< std::string > userIncludes
forced includes given by the user
Definition: settings.h:208
static std::string toNativeSeparators(std::string path)
Convert path to use native separators.
Definition: path.cpp:45
void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro)
static bool missingIncludeFlag
Definition: preprocessor.h:86
static simplecpp::DUI createDUI(const Settings &_settings, const std::string &cfg, const std::string &filename)