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