Cppcheck
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
preprocessor.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2016 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 
20 #include "preprocessor.h"
21 
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 ? 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  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  return true;
554  case simplecpp::Output::WARNING:
555  case simplecpp::Output::MISSING_HEADER:
556  case simplecpp::Output::PORTABILITY_BACKSLASH:
557  break;
558  };
559  }
560  return false;
561 }
562 
563 
564 void Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector<std::string> &files)
565 {
566  const simplecpp::DUI dui = createDUI(_settings, emptyString, files[0]);
567 
568  simplecpp::OutputList outputList;
569 
570  tokenlists = simplecpp::load(rawtokens, files, dui, &outputList);
571 }
572 
574 {
575  for (std::map<std::string, simplecpp::TokenList*>::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
576  if (it->second)
577  it->second->removeComments();
578  }
579 }
580 
581 void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const
582 {
583  tokens->sizeOfType["bool"] = _settings.sizeof_bool;
584  tokens->sizeOfType["short"] = _settings.sizeof_short;
585  tokens->sizeOfType["int"] = _settings.sizeof_int;
586  tokens->sizeOfType["long"] = _settings.sizeof_long;
587  tokens->sizeOfType["long long"] = _settings.sizeof_long_long;
588  tokens->sizeOfType["float"] = _settings.sizeof_float;
589  tokens->sizeOfType["double"] = _settings.sizeof_double;
590  tokens->sizeOfType["long double"] = _settings.sizeof_long_double;
591  tokens->sizeOfType["bool *"] = _settings.sizeof_pointer;
592  tokens->sizeOfType["short *"] = _settings.sizeof_pointer;
593  tokens->sizeOfType["int *"] = _settings.sizeof_pointer;
594  tokens->sizeOfType["long *"] = _settings.sizeof_pointer;
595  tokens->sizeOfType["long long *"] = _settings.sizeof_pointer;
596  tokens->sizeOfType["float *"] = _settings.sizeof_pointer;
597  tokens->sizeOfType["double *"] = _settings.sizeof_pointer;
598  tokens->sizeOfType["long double *"] = _settings.sizeof_pointer;
599 }
600 
601 simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files)
602 {
603  const simplecpp::DUI dui = createDUI(_settings, cfg, files[0]);
604 
605  simplecpp::OutputList outputList;
606  std::list<simplecpp::MacroUsage> macroUsage;
607  simplecpp::TokenList tokens2(files);
608  simplecpp::preprocess(tokens2, tokens1, files, tokenlists, dui, &outputList, &macroUsage);
609 
610  bool showerror = (!_settings.userDefines.empty() && !_settings.force);
611  reportOutput(outputList, showerror);
612  if (hasErrors(outputList))
613  return simplecpp::TokenList(files);
614 
615  tokens2.removeComments();
616 
617  // ensure that guessed define macros without value are not used in the code
618  if (!validateCfg(cfg, macroUsage))
619  return simplecpp::TokenList(files);
620 
621  return tokens2;
622 }
623 
624 std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, const bool writeLocations)
625 {
626  simplecpp::TokenList tokens2 = preprocess(tokens1, cfg, files);
627  unsigned int prevfile = 0;
628  unsigned int line = 1;
629  std::ostringstream ret;
630  for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) {
631  if (writeLocations && tok->location.fileIndex != prevfile) {
632  ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
633  prevfile = tok->location.fileIndex;
634  line = tok->location.line;
635  }
636 
637  if (tok->previous && line >= tok->location.line) // #7912
638  ret << ' ';
639  while (tok->location.line > line) {
640  ret << '\n';
641  line++;
642  }
643  if (!tok->macro.empty())
645  ret << tok->str;
646  }
647 
648  return ret.str();
649 }
650 
651 std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename)
652 {
653  simplecpp::OutputList outputList;
654  std::vector<std::string> files;
655 
656  std::istringstream istr(filedata);
657  simplecpp::TokenList tokens1(istr, files, Path::simplifyPath(filename), &outputList);
658  inlineSuppressions(tokens1);
659  tokens1.removeComments();
660  removeComments();
661  setDirectives(tokens1);
662 
663  reportOutput(outputList, true);
664 
665  if (hasErrors(outputList))
666  return "";
667 
668  return getcode(tokens1, cfg, files, filedata.find("#file") != std::string::npos);
669 }
670 
671 void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror)
672 {
673  for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) {
674  switch (it->type) {
675  case simplecpp::Output::ERROR:
676  if (it->msg.compare(0,6,"#error")!=0 || showerror)
677  error(it->location.file(), it->location.line, it->msg);
678  break;
679  case simplecpp::Output::WARNING:
680  case simplecpp::Output::PORTABILITY_BACKSLASH:
681  break;
682  case simplecpp::Output::MISSING_HEADER: {
683  const std::string::size_type pos1 = it->msg.find_first_of("<\"");
684  const std::string::size_type pos2 = it->msg.find_first_of(">\"", pos1 + 1U);
685  if (pos1 < pos2 && pos2 != std::string::npos)
686  missingInclude(it->location.file(), it->location.line, it->msg.substr(pos1+1, pos2-pos1-1), it->msg[pos1] == '\"' ? UserHeader : SystemHeader);
687  }
688  break;
689  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
690  case simplecpp::Output::SYNTAX_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) != 0) {
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 }
virtual void reportErr(const ErrorLogger::ErrorMessage &msg)=0
Information about found errors and warnings is directed here.
void simplifyPragmaAsm(simplecpp::TokenList *tokenList)
static std::string toxml(const std::string &str)
Convert XML-sensitive characters into XML entities.
Wrapper for error messages, provided by reportErr()
Definition: errorlogger.h:182
bool validateCfg(const std::string &cfg, const std::list< simplecpp::MacroUsage > &macroUsageList)
make sure empty configuration macros are not used in code.
static const simplecpp::Token * gotoEndIf(const simplecpp::Token *cmdtok)
void error(const std::string &filename, unsigned int linenr, const std::string &msg)
void inlineSuppressions(const simplecpp::TokenList &tokens)
unsigned int sizeof_bool
bits in long long
Definition: platform.h:68
std::list< std::string > includePaths
List of include paths, e.g.
Definition: settings.h:170
HeaderTypes
Include file types.
Definition: preprocessor.h:75
std::string str
the actual directive text
Definition: preprocessor.h:55
Programming error.
Definition: errorlogger.h:80
static bool hasDefine(const std::string &userDefines, const std::string &cfg)
static const std::uint32_t crc32Table[]
unsigned int sizeof_long
Definition: platform.h:71
void removeComments()
virtual void reportInfo(const ErrorLogger::ErrorMessage &msg)
Output information messages.
Definition: errorlogger.h:352
bool relativePaths
Use relative paths in output.
Definition: settings.h:130
static std::string fromNativeSeparators(std::string path)
Convert path to use internal path separators.
Definition: path.cpp:65
Checking information.
Definition: errorlogger.h:114
static std::string readcondition(const simplecpp::Token *iftok, const std::set< std::string > &defined, const std::set< std::string > &undefined)
unsigned int sizeof_short
Definition: platform.h:69
Settings & _settings
Definition: preprocessor.h:209
virtual ~Preprocessor()
The cppcheck preprocessor.
Definition: preprocessor.h:69
unsigned int sizeof_long_long
Definition: platform.h:72
Directive(const std::string &_file, const int _linenr, const std::string &_str)
record a directive (possibly filtering src)
static std::string simplifyPath(std::string originalPath)
Simplify path "foo/bar/.." => "foo".
Definition: path.cpp:73
static bool isUndefined(const std::string &cfg, const std::set< std::string > &undefined)
static std::string trim(const std::string &s)
Remove heading and trailing whitespaces from the input parameter.
static std::uint32_t crc32(const std::string &data)
void setDirectives(const simplecpp::TokenList &tokens)
static void splitcfg(const std::string &cfg, std::list< std::string > &defines, const std::string &defaultValue)
static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
std::string addSuppression(const std::string &errorId, const std::string &file=emptyString, unsigned int line=0)
Don't show this error.
unsigned int sizeof_float
Definition: platform.h:73
static const std::string emptyString
Definition: config.h:23
std::list< Directive > directives
list of all directives met while preprocessing file
Definition: preprocessor.h:213
bool checkConfiguration
Is the 'configuration checking' wanted?
Definition: settings.h:267
static void inlineSuppressions(const simplecpp::TokenList &tokens, Settings &_settings)
bool isEnabled(EnabledGroup group) const
Returns true if given id is in the list of enabled extra checks (–enable)
Definition: settings.h:182
This is an interface, which the class responsible of error logging should implement.
Definition: errorlogger.h:174
unsigned int sizeof_pointer
Definition: platform.h:78
bool isSuppressed(const std::string &errorId, const std::string &file, unsigned int line)
Returns true if this message should not be shown to the user.
static void getConfigs(const simplecpp::TokenList &tokens, std::set< std::string > &defined, const std::string &userDefines, const std::set< std::string > &undefined, std::set< std::string > &ret)
Suppressions nomsg
suppress message (–suppressions)
Definition: settings.h:221
std::set< std::string > userUndefs
undefines given by the user
Definition: settings.h:230
static void preprocessWhitespaces(std::string &processedFile)
preprocess all whitespaces
unsigned int sizeof_double
Definition: platform.h:74
std::vector< std::string > defines
Definition: library.h:370
static bool missingSystemIncludeFlag
Definition: preprocessor.h:88
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:50
void preprocess(std::istream &istr, std::map< std::string, std::string > &result, const std::string &filename, const std::list< std::string > &includePaths=std::list< std::string >())
Extract the code for each configuration.
std::string getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector< std::string > &files, const bool writeLocations)
static std::string removeSpaceNearNL(const std::string &str)
Remove space that has new line character on left or right side of it.
std::map< std::string, simplecpp::TokenList * > tokenlists
Definition: preprocessor.h:215
Library library
Library (–library)
Definition: settings.h:243
static char macroChar
character that is inserted in expanded macros
Definition: preprocessor.h:82
A preprocessor directive Each preprocessor directive (#include, #define, #undef, #if, #ifdef, #else, #endif) will be recorded as an instance of this class.
Definition: preprocessor.h:46
std::set< std::string > getConfigs(const simplecpp::TokenList &tokens) const
static bool getConfigsElseIsFalse(const std::vector< std::string > &configs_if, const std::string &userDefines)
std::string userDefines
defines given by the user
Definition: settings.h:227
Preprocessor(Settings &settings, ErrorLogger *errorLogger=nullptr)
void reportOutput(const simplecpp::OutputList &outputList, bool showerror)
unsigned int calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
Calculate CRC32 checksum.
void dump(std::ostream &out) const
dump all directives present in source file
unsigned int sizeof_int
Definition: platform.h:70
void setPlatformInfo(simplecpp::TokenList *tokens) const
ErrorLogger * _errorLogger
Definition: preprocessor.h:210
bool configurationExcluded(const std::string &file) const
return true if a included file is to be excluded in Preprocessor::getConfigs
Definition: settings.h:281
static bool hasErrors(const simplecpp::OutputList &outputList)
static void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
File name and line number.
Definition: errorlogger.h:189
unsigned int sizeof_long_double
Definition: platform.h:75
bool inlineSuppressions
Is –inline-suppr given?
Definition: settings.h:111
static bool isCPP(const std::string &path)
Identify language based on file extension.
Definition: path.cpp:195
static std::string cfg(const std::vector< std::string > &configs, const std::string &userDefines)
void missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
std::string file0
filename for cpp/c file - useful when reporting errors
Definition: preprocessor.h:218
void loadFiles(const simplecpp::TokenList &rawtokens, std::vector< std::string > &files)
bool force
Force checking the files with "too many" configurations (–force).
Definition: settings.h:127
std::vector< std::string > basePaths
Paths used as base for conversion to relative paths.
Definition: settings.h:133
void setfile(const std::string &file)
Set the filename.
std::list< std::string > userIncludes
forced includes given by the user
Definition: settings.h:233
static std::string toNativeSeparators(std::string path)
Convert path to use native separators.
Definition: path.cpp:52
void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro)
static bool missingIncludeFlag
Definition: preprocessor.h:87
static void simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList)
static simplecpp::DUI createDUI(const Settings &_settings, const std::string &cfg, const std::string &filename)