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->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  simplecpp::OutputList outputList;
570 
571  tokenlists = simplecpp::load(rawtokens, files, dui, &outputList);
572 }
573 
575 {
576  for (std::map<std::string, simplecpp::TokenList*>::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
577  if (it->second)
578  it->second->removeComments();
579  }
580 }
581 
582 void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const
583 {
584  tokens->sizeOfType["bool"] = _settings.sizeof_bool;
585  tokens->sizeOfType["short"] = _settings.sizeof_short;
586  tokens->sizeOfType["int"] = _settings.sizeof_int;
587  tokens->sizeOfType["long"] = _settings.sizeof_long;
588  tokens->sizeOfType["long long"] = _settings.sizeof_long_long;
589  tokens->sizeOfType["float"] = _settings.sizeof_float;
590  tokens->sizeOfType["double"] = _settings.sizeof_double;
591  tokens->sizeOfType["long double"] = _settings.sizeof_long_double;
592  tokens->sizeOfType["bool *"] = _settings.sizeof_pointer;
593  tokens->sizeOfType["short *"] = _settings.sizeof_pointer;
594  tokens->sizeOfType["int *"] = _settings.sizeof_pointer;
595  tokens->sizeOfType["long *"] = _settings.sizeof_pointer;
596  tokens->sizeOfType["long long *"] = _settings.sizeof_pointer;
597  tokens->sizeOfType["float *"] = _settings.sizeof_pointer;
598  tokens->sizeOfType["double *"] = _settings.sizeof_pointer;
599  tokens->sizeOfType["long double *"] = _settings.sizeof_pointer;
600 }
601 
602 simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files)
603 {
604  const simplecpp::DUI dui = createDUI(_settings, cfg, files[0]);
605 
606  simplecpp::OutputList outputList;
607  std::list<simplecpp::MacroUsage> macroUsage;
608  simplecpp::TokenList tokens2(files);
609  simplecpp::preprocess(tokens2, tokens1, files, tokenlists, dui, &outputList, &macroUsage);
610 
611  bool showerror = (!_settings.userDefines.empty() && !_settings.force);
612  reportOutput(outputList, showerror);
613  if (hasErrors(outputList))
614  return simplecpp::TokenList(files);
615 
616  tokens2.removeComments();
617 
618  // ensure that guessed define macros without value are not used in the code
619  if (!validateCfg(cfg, macroUsage))
620  return simplecpp::TokenList(files);
621 
622  return tokens2;
623 }
624 
625 std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, const bool writeLocations)
626 {
627  simplecpp::TokenList tokens2 = preprocess(tokens1, cfg, files);
628  unsigned int prevfile = 0;
629  unsigned int line = 1;
630  std::ostringstream ret;
631  for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) {
632  if (writeLocations && tok->location.fileIndex != prevfile) {
633  ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
634  prevfile = tok->location.fileIndex;
635  line = tok->location.line;
636  }
637 
638  if (tok->previous && line >= tok->location.line) // #7912
639  ret << ' ';
640  while (tok->location.line > line) {
641  ret << '\n';
642  line++;
643  }
644  if (!tok->macro.empty())
646  ret << tok->str;
647  }
648 
649  return ret.str();
650 }
651 
652 std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename)
653 {
654  simplecpp::OutputList outputList;
655  std::vector<std::string> files;
656 
657  std::istringstream istr(filedata);
658  simplecpp::TokenList tokens1(istr, files, Path::simplifyPath(filename), &outputList);
659  inlineSuppressions(tokens1);
660  tokens1.removeComments();
661  removeComments();
662  setDirectives(tokens1);
663 
664  reportOutput(outputList, true);
665 
666  if (hasErrors(outputList))
667  return "";
668 
669  return getcode(tokens1, cfg, files, filedata.find("#file") != std::string::npos);
670 }
671 
672 void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror)
673 {
674  for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) {
675  switch (it->type) {
676  case simplecpp::Output::ERROR:
677  if (it->msg.compare(0,6,"#error")!=0 || showerror)
678  error(it->location.file(), it->location.line, it->msg);
679  break;
680  case simplecpp::Output::WARNING:
681  case simplecpp::Output::PORTABILITY_BACKSLASH:
682  break;
683  case simplecpp::Output::MISSING_HEADER: {
684  const std::string::size_type pos1 = it->msg.find_first_of("<\"");
685  const std::string::size_type pos2 = it->msg.find_first_of(">\"", pos1 + 1U);
686  if (pos1 < pos2 && pos2 != std::string::npos)
687  missingInclude(it->location.file(), it->location.line, it->msg.substr(pos1+1, pos2-pos1-1), it->msg[pos1] == '\"' ? UserHeader : SystemHeader);
688  }
689  break;
690  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
691  case simplecpp::Output::SYNTAX_ERROR:
692  case simplecpp::Output::UNHANDLED_CHAR_ERROR:
693  error(it->location.file(), it->location.line, it->msg);
694  break;
695  };
696  }
697 }
698 
699 void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg)
700 {
701  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
702  if (!filename.empty()) {
703  ErrorLogger::ErrorMessage::FileLocation loc(filename, linenr);
704  locationList.push_back(loc);
705  }
707  file0,
709  msg,
710  "preprocessorErrorDirective",
711  false));
712 }
713 
714 // Report that include is missing
715 void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
716 {
717  const std::string fname = Path::fromNativeSeparators(filename);
718  if (_settings.nomsg.isSuppressed("missingInclude", fname, linenr))
719  return;
720  if (headerType == SystemHeader && _settings.nomsg.isSuppressed("missingIncludeSystem", fname, linenr))
721  return;
722 
723  if (headerType == SystemHeader)
725  else
726  missingIncludeFlag = true;
728 
729  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
730  if (!filename.empty()) {
732  loc.line = linenr;
733  loc.setfile(Path::toNativeSeparators(filename));
734  locationList.push_back(loc);
735  }
737  (headerType==SystemHeader) ?
738  "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." :
739  "Include file: \"" + header + "\" not found.",
740  (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude",
741  false);
742  _errorLogger->reportInfo(errmsg);
743  }
744 }
745 
746 bool Preprocessor::validateCfg(const std::string &cfg, const std::list<simplecpp::MacroUsage> &macroUsageList)
747 {
748  bool ret = true;
749  std::list<std::string> defines;
750  splitcfg(cfg, defines, emptyString);
751  for (std::list<std::string>::const_iterator defineIt = defines.begin(); defineIt != defines.end(); ++defineIt) {
752  if (defineIt->find('=') != std::string::npos)
753  continue;
754  const std::string macroName(defineIt->substr(0, defineIt->find('(')));
755  for (std::list<simplecpp::MacroUsage>::const_iterator usageIt = macroUsageList.begin(); usageIt != macroUsageList.end(); ++usageIt) {
756  const simplecpp::MacroUsage &mu = *usageIt;
757  if (mu.macroName != macroName)
758  continue;
759  bool directiveLocation = false;
760  for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
761  if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) {
762  directiveLocation = true;
763  break;
764  }
765  }
766  if (!directiveLocation) {
768  validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName);
769  ret = false;
770  }
771  }
772  }
773 
774  return ret;
775 }
776 
777 void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro)
778 {
779  const std::string id = "ConfigurationNotChecked";
780  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
782  locationList.push_back(loc);
783  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);
784  _errorLogger->reportInfo(errmsg);
785 }
786 
787 void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
788 {
789  Settings settings2(*settings);
790  Preprocessor preprocessor(settings2, errorLogger);
791  settings2.checkConfiguration=true;
794  preprocessor.validateCfgError(emptyString, 1, "X", "X");
795  preprocessor.error(emptyString, 1, "#error message"); // #error ..
796 }
797 
798 void Preprocessor::dump(std::ostream &out) const
799 {
800  // Create a xml directive dump.
801  // The idea is not that this will be readable for humans. It's a
802  // data dump that 3rd party tools could load and get useful info from.
803  out << " <directivelist>" << std::endl;
804 
805  for (std::list<Directive>::const_iterator it = directives.begin(); it != directives.end(); ++it) {
806  out << " <directive "
807  << "file=\"" << it->file << "\" "
808  << "linenr=\"" << it->linenr << "\" "
809  // str might contain characters such as '"', '<' or '>' which
810  // could result in invalid XML, so run it through toxml().
811  << "str=\"" << ErrorLogger::toxml(it->str) << "\"/>" << std::endl;
812  }
813  out << " </directivelist>" << std::endl;
814 }
815 
816 static const std::uint32_t crc32Table[] = {
817  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
818  0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
819  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
820  0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
821  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
822  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
823  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
824  0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
825  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
826  0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
827  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
828  0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
829  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
830  0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
831  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
832  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
833  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
834  0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
835  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
836  0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
837  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
838  0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
839  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
840  0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
841  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
842  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
843  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
844  0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
845  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
846  0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
847  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
848  0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
849  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
850  0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
851  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
852  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
853  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
854  0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
855  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
856  0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
857  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
858  0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
859  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
860 };
861 
862 static std::uint32_t crc32(const std::string &data)
863 {
864  std::uint32_t crc = ~0U;
865  for (std::string::const_iterator c = data.begin(); c != data.end(); ++c) {
866  crc = crc32Table[(crc ^ (unsigned char)(*c)) & 0xFF] ^ (crc >> 8);
867  }
868  return crc ^ ~0U;
869 }
870 
871 unsigned int Preprocessor::calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
872 {
873  std::ostringstream ostr;
874  ostr << toolinfo << '\n';
875  for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
876  if (!tok->comment)
877  ostr << tok->str;
878  }
879  for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
880  for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) {
881  if (!tok->comment)
882  ostr << tok->str;
883  }
884  }
885  return crc32(ostr.str());
886 }
887 
888 void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList *tokenList)
889 {
891  for (std::map<std::string, simplecpp::TokenList *>::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
893  }
894 }
895 
896 void Preprocessor::simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList)
897 {
898  // assembler code..
899  for (simplecpp::Token *tok = tokenList->front(); tok; tok = tok->next) {
900  if (tok->op != '#')
901  continue;
902  if (sameline(tok, tok->previousSkipComments()))
903  continue;
904 
905  const simplecpp::Token * const tok2 = tok->nextSkipComments();
906  if (!tok2 || !sameline(tok, tok2) || tok2->str != "pragma")
907  continue;
908 
909  const simplecpp::Token * const tok3 = tok2->nextSkipComments();
910  if (!tok3 || !sameline(tok, tok3) || tok3->str != "asm")
911  continue;
912 
913  const simplecpp::Token *endasm = tok3;
914  while ((endasm = endasm->next) != nullptr) {
915  if (endasm->op != '#' || sameline(endasm,endasm->previousSkipComments()))
916  continue;
917  const simplecpp::Token * const endasm2 = endasm->nextSkipComments();
918  if (!endasm2 || !sameline(endasm, endasm2) || endasm2->str != "pragma")
919  continue;
920  const simplecpp::Token * const endasm3 = endasm2->nextSkipComments();
921  if (!endasm3 || !sameline(endasm2, endasm3) || endasm3->str != "endasm")
922  continue;
923  while (sameline(endasm,endasm3))
924  endasm = endasm->next;
925  break;
926  }
927 
928  const simplecpp::Token * const tok4 = tok3->next;
929  tok->setstr("asm");
930  const_cast<simplecpp::Token *>(tok2)->setstr("(");
931  const_cast<simplecpp::Token *>(tok3)->setstr(")");
932  const_cast<simplecpp::Token *>(tok4)->setstr(";");
933  while (tok4->next != endasm)
934  tokenList->deleteToken(tok4->next);
935  }
936 }
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:171
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:350
bool relativePaths
Use relative paths in output.
Definition: settings.h:131
static std::string fromNativeSeparators(std::string path)
Convert path to use internal path separators.
Definition: path.cpp:66
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:74
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:268
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:183
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:222
std::set< std::string > userUndefs
undefines given by the user
Definition: settings.h:231
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:371
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:244
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:228
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:282
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:112
static bool isCPP(const std::string &path)
Identify language based on file extension.
Definition: path.cpp:185
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:128
std::vector< std::string > basePaths
Paths used as base for conversion to relative paths.
Definition: settings.h:134
void setfile(const std::string &file)
Set the filename.
std::list< std::string > userIncludes
forced includes given by the user
Definition: settings.h:234
static std::string toNativeSeparators(std::string path)
Convert path to use native separators.
Definition: path.cpp:53
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)