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-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 
25 #include <algorithm>
26 #include <functional>
27 #include <sstream>
28 #include <cstdlib>
29 #include <cctype>
30 #include <vector>
31 #include <set>
32 #include <cstdint>
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 ? tok->next : nullptr) {
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  if (!elseError.empty())
356  elseError += ';';
357  elseError += cfg(configs_if, userDefines);
358  }
359  if (!configs_if.empty())
360  configs_if.pop_back();
361  if (cmdtok->str == "elif") {
362  std::string config = readcondition(cmdtok, defined, undefined);
363  if (isUndefined(config,undefined))
364  config.clear();
365  configs_if.push_back(config);
366  ret.insert(cfg(configs_if, userDefines));
367  } else if (!configs_ifndef.empty()) {
368  configs_if.push_back(configs_ifndef.back());
369  ret.insert(cfg(configs_if, userDefines));
370  }
371  } else if (cmdtok->str == "endif" && !sameline(tok, cmdtok->next)) {
372  if (!configs_if.empty())
373  configs_if.pop_back();
374  if (!configs_ifndef.empty())
375  configs_ifndef.pop_back();
376  } else if (cmdtok->str == "error") {
377  if (!configs_ifndef.empty() && !configs_ifndef.back().empty()) {
378  if (configs_ifndef.size() == 1U)
379  ret.erase("");
380  std::vector<std::string> configs(configs_if);
381  configs.push_back(configs_ifndef.back());
382  ret.erase(cfg(configs, userDefines));
383  if (!elseError.empty())
384  elseError += ';';
385  elseError += cfg(configs_ifndef, userDefines);
386  }
387  if (!configs_if.empty() && !configs_if.back().empty()) {
388  const std::string &last = configs_if.back();
389  if (last.size() > 2U && last.compare(last.size()-2U,2,"=0") == 0) {
390  std::vector<std::string> configs(configs_if);
391  ret.erase(cfg(configs, userDefines));
392  configs[configs.size() - 1U] = last.substr(0,last.size()-2U);
393  if (configs.size() == 1U)
394  ret.erase("");
395  if (!elseError.empty())
396  elseError += ';';
397  elseError += cfg(configs, userDefines);
398  }
399  }
400  } else if (cmdtok->str == "define" && sameline(tok, cmdtok->next) && cmdtok->next->name) {
401  defined.insert(cmdtok->next->str);
402  }
403  }
404  if (!elseError.empty())
405  ret.insert(elseError);
406 }
407 
408 
409 std::set<std::string> Preprocessor::getConfigs(const simplecpp::TokenList &tokens) const
410 {
411  std::set<std::string> ret;
412  ret.insert("");
413  if (!tokens.cfront())
414  return ret;
415 
416  std::set<std::string> defined;
417  defined.insert("__cplusplus");
418 
419  ::getConfigs(tokens, defined, _settings.userDefines, _settings.userUndefs, ret);
420 
421  for (std::map<std::string, simplecpp::TokenList*>::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
422  if (!_settings.configurationExcluded(it->first))
423  ::getConfigs(*(it->second), defined, _settings.userDefines, _settings.userUndefs, ret);
424  }
425 
426  return ret;
427 }
428 
429 
430 void Preprocessor::preprocess(std::istream &istr, std::map<std::string, std::string> &result, const std::string &filename, const std::list<std::string> &includePaths)
431 {
432  (void)includePaths;
433 
434  simplecpp::OutputList outputList;
435  std::vector<std::string> files;
436  const simplecpp::TokenList tokens1(istr, files, filename, &outputList);
437 
438  const std::set<std::string> configs = getConfigs(tokens1);
439 
440  for (std::set<std::string>::const_iterator it = configs.begin(); it != configs.end(); ++it) {
441  if (_settings.userUndefs.find(*it) == _settings.userUndefs.end()) {
442  result[ *it ] = getcode(tokens1, *it, files, false);
443  }
444  }
445 }
446 
447 std::string Preprocessor::removeSpaceNearNL(const std::string &str)
448 {
449  std::string tmp;
450  char prev = '\n'; // treat start of file as newline
451  for (std::size_t i = 0; i < str.size(); i++) {
452  if (str[i] == ' ' &&
453  (prev == '\n' ||
454  i + 1 >= str.size() || // treat end of file as newline
455  str[i+1] == '\n'
456  )
457  ) {
458  // Ignore space that has new line in either side of it
459  } else {
460  tmp.append(1, str[i]);
461  prev = str[i];
462  }
463  }
464 
465  return tmp;
466 }
467 
468 void Preprocessor::preprocessWhitespaces(std::string &processedFile)
469 {
470  // Replace all tabs with spaces..
471  std::replace(processedFile.begin(), processedFile.end(), '\t', ' ');
472 
473  // Remove space characters that are after or before new line character
474  processedFile = removeSpaceNearNL(processedFile);
475 }
476 
477 void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list<std::string> &resultConfigurations, const std::string &filename, const std::list<std::string> &includePaths)
478 {
479  (void)includePaths;
480 
481  if (file0.empty())
482  file0 = filename;
483 
484  simplecpp::OutputList outputList;
485  std::vector<std::string> files;
486  const simplecpp::TokenList tokens1(srcCodeStream, files, filename, &outputList);
487 
488  const std::set<std::string> configs = getConfigs(tokens1);
489  for (std::set<std::string>::const_iterator it = configs.begin(); it != configs.end(); ++it)
490  resultConfigurations.push_back(*it);
491 
492  processedFile = tokens1.stringify();
493 }
494 
495 static void splitcfg(const std::string &cfg, std::list<std::string> &defines, const std::string &defaultValue)
496 {
497  for (std::string::size_type defineStartPos = 0U; defineStartPos < cfg.size();) {
498  const std::string::size_type defineEndPos = cfg.find(';', defineStartPos);
499  std::string def = (defineEndPos == std::string::npos) ? cfg.substr(defineStartPos) : cfg.substr(defineStartPos, defineEndPos - defineStartPos);
500  if (!defaultValue.empty() && def.find('=') == std::string::npos)
501  def += '=' + defaultValue;
502  defines.push_back(def);
503  if (defineEndPos == std::string::npos)
504  break;
505  defineStartPos = defineEndPos + 1U;
506  }
507 }
508 
509 static simplecpp::DUI createDUI(const Settings &_settings, const std::string &cfg, const std::string &filename)
510 {
511  simplecpp::DUI dui;
512 
513  splitcfg(_settings.userDefines, dui.defines, "1");
514  if (!cfg.empty())
515  splitcfg(cfg, dui.defines, emptyString);
516 
517  for (std::vector<std::string>::const_iterator it = _settings.library.defines.begin(); it != _settings.library.defines.end(); ++it) {
518  if (it->compare(0,8,"#define ")!=0)
519  continue;
520  std::string s = it->substr(8);
521  std::string::size_type pos = s.find_first_of(" (");
522  if (pos == std::string::npos) {
523  dui.defines.push_back(s);
524  continue;
525  }
526  if (s[pos] == ' ') {
527  s[pos] = '=';
528  } else {
529  s[s.find(')')+1] = '=';
530  }
531  dui.defines.push_back(s);
532  }
533 
534  if (Path::isCPP(filename))
535  dui.defines.push_back("__cplusplus");
536 
537  dui.undefined = _settings.userUndefs; // -U
538  dui.includePaths = _settings.includePaths; // -I
539  dui.includes = _settings.userIncludes; // --include
540  return dui;
541 }
542 
543 static bool hasErrors(const simplecpp::OutputList &outputList)
544 {
545  for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) {
546  switch (it->type) {
547  case simplecpp::Output::ERROR:
548  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
549  case simplecpp::Output::SYNTAX_ERROR:
550  return true;
551  case simplecpp::Output::WARNING:
552  case simplecpp::Output::MISSING_HEADER:
553  case simplecpp::Output::PORTABILITY_BACKSLASH:
554  break;
555  };
556  }
557  return false;
558 }
559 
560 
561 void Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector<std::string> &files)
562 {
563  const simplecpp::DUI dui = createDUI(_settings, emptyString, files[0]);
564 
565  simplecpp::OutputList outputList;
566 
567  tokenlists = simplecpp::load(rawtokens, files, dui, &outputList);
568 }
569 
571 {
572  for (std::map<std::string, simplecpp::TokenList*>::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
573  if (it->second)
574  it->second->removeComments();
575  }
576 }
577 
578 void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const
579 {
580  tokens->sizeOfType["bool"] = _settings.sizeof_bool;
581  tokens->sizeOfType["short"] = _settings.sizeof_short;
582  tokens->sizeOfType["int"] = _settings.sizeof_int;
583  tokens->sizeOfType["long"] = _settings.sizeof_long;
584  tokens->sizeOfType["long long"] = _settings.sizeof_long_long;
585  tokens->sizeOfType["float"] = _settings.sizeof_float;
586  tokens->sizeOfType["double"] = _settings.sizeof_double;
587  tokens->sizeOfType["long double"] = _settings.sizeof_long_double;
588  tokens->sizeOfType["bool *"] = _settings.sizeof_pointer;
589  tokens->sizeOfType["short *"] = _settings.sizeof_pointer;
590  tokens->sizeOfType["int *"] = _settings.sizeof_pointer;
591  tokens->sizeOfType["long *"] = _settings.sizeof_pointer;
592  tokens->sizeOfType["long long *"] = _settings.sizeof_pointer;
593  tokens->sizeOfType["float *"] = _settings.sizeof_pointer;
594  tokens->sizeOfType["double *"] = _settings.sizeof_pointer;
595  tokens->sizeOfType["long double *"] = _settings.sizeof_pointer;
596 }
597 
598 simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files)
599 {
600  const simplecpp::DUI dui = createDUI(_settings, cfg, files[0]);
601 
602  simplecpp::OutputList outputList;
603  std::list<simplecpp::MacroUsage> macroUsage;
604  simplecpp::TokenList tokens2(files);
605  simplecpp::preprocess(tokens2, tokens1, files, tokenlists, dui, &outputList, &macroUsage);
606 
607  bool showerror = (!_settings.userDefines.empty() && !_settings.force);
608  reportOutput(outputList, showerror);
609  if (hasErrors(outputList))
610  return simplecpp::TokenList(files);
611 
612  tokens2.removeComments();
613 
614  // ensure that guessed define macros without value are not used in the code
615  if (!validateCfg(cfg, macroUsage))
616  return simplecpp::TokenList(files);
617 
618  return tokens2;
619 }
620 
621 std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, const bool writeLocations)
622 {
623  simplecpp::TokenList tokens2 = preprocess(tokens1, cfg, files);
624  unsigned int prevfile = 0;
625  unsigned int line = 1;
626  std::ostringstream ret;
627  for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) {
628  if (writeLocations && tok->location.fileIndex != prevfile) {
629  ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
630  prevfile = tok->location.fileIndex;
631  line = tok->location.line;
632  }
633 
634  if (tok->previous && line >= tok->location.line) // #7912
635  ret << ' ';
636  while (tok->location.line > line) {
637  ret << '\n';
638  line++;
639  }
640  if (!tok->macro.empty())
642  ret << tok->str;
643  }
644 
645  return ret.str();
646 }
647 
648 std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename)
649 {
650  simplecpp::OutputList outputList;
651  std::vector<std::string> files;
652 
653  std::istringstream istr(filedata);
654  simplecpp::TokenList tokens1(istr, files, Path::simplifyPath(filename), &outputList);
655  inlineSuppressions(tokens1);
656  tokens1.removeComments();
657  removeComments();
658  setDirectives(tokens1);
659 
660  reportOutput(outputList, true);
661 
662  if (hasErrors(outputList))
663  return "";
664 
665  return getcode(tokens1, cfg, files, filedata.find("#file") != std::string::npos);
666 }
667 
668 void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror)
669 {
670  for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) {
671  switch (it->type) {
672  case simplecpp::Output::ERROR:
673  if (it->msg.compare(0,6,"#error")!=0 || showerror)
674  error(it->location.file(), it->location.line, it->msg);
675  break;
676  case simplecpp::Output::WARNING:
677  case simplecpp::Output::PORTABILITY_BACKSLASH:
678  break;
679  case simplecpp::Output::MISSING_HEADER: {
680  const std::string::size_type pos1 = it->msg.find_first_of("<\"");
681  const std::string::size_type pos2 = it->msg.find_first_of(">\"", pos1 + 1U);
682  if (pos1 < pos2 && pos2 != std::string::npos)
683  missingInclude(it->location.file(), it->location.line, it->msg.substr(pos1+1, pos2-pos1-1), it->msg[pos1] == '\"' ? UserHeader : SystemHeader);
684  }
685  break;
686  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
687  case simplecpp::Output::SYNTAX_ERROR:
688  error(it->location.file(), it->location.line, it->msg);
689  break;
690  };
691  }
692 }
693 
694 void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg)
695 {
696  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
697  if (!filename.empty()) {
698  ErrorLogger::ErrorMessage::FileLocation loc(filename, linenr);
699  locationList.push_back(loc);
700  }
702  file0,
704  msg,
705  "preprocessorErrorDirective",
706  false));
707 }
708 
709 // Report that include is missing
710 void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
711 {
712  const std::string fname = Path::fromNativeSeparators(filename);
713  if (_settings.nomsg.isSuppressed("missingInclude", fname, linenr))
714  return;
715  if (headerType == SystemHeader && _settings.nomsg.isSuppressed("missingIncludeSystem", fname, linenr))
716  return;
717 
718  if (headerType == SystemHeader)
720  else
721  missingIncludeFlag = true;
723 
724  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
725  if (!filename.empty()) {
727  loc.line = linenr;
728  loc.setfile(Path::toNativeSeparators(filename));
729  locationList.push_back(loc);
730  }
732  (headerType==SystemHeader) ?
733  "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." :
734  "Include file: \"" + header + "\" not found.",
735  (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude",
736  false);
737  _errorLogger->reportInfo(errmsg);
738  }
739 }
740 
741 bool Preprocessor::validateCfg(const std::string &cfg, const std::list<simplecpp::MacroUsage> &macroUsageList)
742 {
743  bool ret = true;
744  std::list<std::string> defines;
745  splitcfg(cfg, defines, emptyString);
746  for (std::list<std::string>::const_iterator defineIt = defines.begin(); defineIt != defines.end(); ++defineIt) {
747  if (defineIt->find('=') != std::string::npos)
748  continue;
749  const std::string macroName(defineIt->substr(0, defineIt->find('(')));
750  for (std::list<simplecpp::MacroUsage>::const_iterator usageIt = macroUsageList.begin(); usageIt != macroUsageList.end(); ++usageIt) {
751  const simplecpp::MacroUsage &mu = *usageIt;
752  if (mu.macroName != macroName)
753  continue;
754  bool directiveLocation = false;
755  for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
756  if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) {
757  directiveLocation = true;
758  break;
759  }
760  }
761  if (!directiveLocation) {
763  validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName);
764  ret = false;
765  }
766  }
767  }
768 
769  return ret;
770 }
771 
772 void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro)
773 {
774  const std::string id = "ConfigurationNotChecked";
775  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
777  locationList.push_back(loc);
778  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);
779  _errorLogger->reportInfo(errmsg);
780 }
781 
782 void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
783 {
784  Settings settings2(*settings);
785  Preprocessor preprocessor(settings2, errorLogger);
786  settings2.checkConfiguration=true;
789  preprocessor.validateCfgError(emptyString, 1, "X", "X");
790  preprocessor.error(emptyString, 1, "#error message"); // #error ..
791 }
792 
793 void Preprocessor::dump(std::ostream &out) const
794 {
795  // Create a xml directive dump.
796  // The idea is not that this will be readable for humans. It's a
797  // data dump that 3rd party tools could load and get useful info from.
798  out << " <directivelist>" << std::endl;
799 
800  for (std::list<Directive>::const_iterator it = directives.begin(); it != directives.end(); ++it) {
801  out << " <directive "
802  << "file=\"" << it->file << "\" "
803  << "linenr=\"" << it->linenr << "\" "
804  // str might contain characters such as '"', '<' or '>' which
805  // could result in invalid XML, so run it through toxml().
806  << "str=\"" << ErrorLogger::toxml(it->str) << "\"/>" << std::endl;
807  }
808  out << " </directivelist>" << std::endl;
809 }
810 
811 static const std::uint32_t crc32Table[] = {
812  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
813  0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
814  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
815  0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
816  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
817  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
818  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
819  0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
820  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
821  0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
822  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
823  0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
824  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
825  0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
826  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
827  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
828  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
829  0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
830  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
831  0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
832  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
833  0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
834  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
835  0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
836  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
837  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
838  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
839  0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
840  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
841  0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
842  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
843  0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
844  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
845  0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
846  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
847  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
848  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
849  0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
850  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
851  0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
852  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
853  0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
854  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
855 };
856 
857 static std::uint32_t crc32(const std::string &data)
858 {
859  std::uint32_t crc = ~0U;
860  for (std::string::const_iterator c = data.begin(); c != data.end(); ++c) {
861  crc = crc32Table[(crc ^ (unsigned char)(*c)) & 0xFF] ^ (crc >> 8);
862  }
863  return crc ^ ~0U;
864 }
865 
866 unsigned int Preprocessor::calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
867 {
868  std::ostringstream ostr;
869  ostr << toolinfo << '\n';
870  for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
871  if (!tok->comment)
872  ostr << tok->str;
873  }
874  for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
875  for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) {
876  if (!tok->comment)
877  ostr << tok->str;
878  }
879  }
880  return crc32(ostr.str());
881 }
882 
883 void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList *tokenList)
884 {
886  for (std::map<std::string, simplecpp::TokenList *>::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
888  }
889 }
890 
891 void Preprocessor::simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList)
892 {
893  // assembler code..
894  for (simplecpp::Token *tok = tokenList->front(); tok; tok = tok->next) {
895  if (tok->op != '#')
896  continue;
897  if (sameline(tok, tok->previousSkipComments()))
898  continue;
899 
900  const simplecpp::Token * const tok2 = tok->nextSkipComments();
901  if (!tok2 || !sameline(tok, tok2) || tok2->str != "pragma")
902  continue;
903 
904  const simplecpp::Token * const tok3 = tok2->nextSkipComments();
905  if (!tok3 || !sameline(tok, tok3) || tok3->str != "asm")
906  continue;
907 
908  const simplecpp::Token *endasm = tok3;
909  while ((endasm = endasm->next) != 0) {
910  if (endasm->op != '#' || sameline(endasm,endasm->previousSkipComments()))
911  continue;
912  const simplecpp::Token * const endasm2 = endasm->nextSkipComments();
913  if (!endasm2 || !sameline(endasm, endasm2) || endasm2->str != "pragma")
914  continue;
915  const simplecpp::Token * const endasm3 = endasm2->nextSkipComments();
916  if (!endasm3 || !sameline(endasm2, endasm3) || endasm3->str != "endasm")
917  continue;
918  while (sameline(endasm,endasm3))
919  endasm = endasm->next;
920  break;
921  }
922 
923  const simplecpp::Token * const tok4 = tok3->next;
924  tok->setstr("asm");
925  const_cast<simplecpp::Token *>(tok2)->setstr("(");
926  const_cast<simplecpp::Token *>(tok3)->setstr(")");
927  const_cast<simplecpp::Token *>(tok4)->setstr(";");
928  while (tok4->next != endasm)
929  tokenList->deleteToken(tok4->next);
930  }
931 }