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