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