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 <sstream>
28 #include <cstdlib>
29 #include <cctype>
30 #include <vector>
31 #include <set>
32 
33 /**
34  * Remove heading and trailing whitespaces from the input parameter.
35  * If string is all spaces/tabs, return empty string.
36  * @param s The string to trim.
37  */
38 static std::string trim(const std::string& s)
39 {
40  const std::string::size_type beg = s.find_first_not_of(" \t");
41  if (beg == std::string::npos)
42  return "";
43  const std::string::size_type end = s.find_last_not_of(" \t");
44  return s.substr(beg, end - beg + 1);
45 }
46 
47 Directive::Directive(const std::string &_file, const int _linenr, const std::string &_str):
48  file(_file),
49  linenr(_linenr),
50  str(_str)
51 {
52  str = trim(str);
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 &tokens1)
122 {
123  // directive list..
124  directives.clear();
125 
126  std::list<const simplecpp::TokenList *> list;
127  list.push_back(&tokens1);
128  for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
129  list.push_back(it->second);
130  }
131 
132  for (std::list<const simplecpp::TokenList *>::const_iterator it = list.begin(); it != list.end(); ++it) {
133  for (const simplecpp::Token *tok = (*it)->cfront(); tok; tok = tok ? tok->next : nullptr) {
134  if ((tok->op != '#') || (tok->previous && tok->previous->location.line == tok->location.line))
135  continue;
136  if (tok->next && tok->next->str == "endfile")
137  continue;
138  Directive directive(tok->location.file(), tok->location.line, "");
139  for (const simplecpp::Token *tok2 = tok; tok2 && tok2->location.line == directive.linenr; tok2 = tok2->next) {
140  if (tok2->comment)
141  continue;
142  if (!directive.str.empty() && (tok2->location.col > tok2->previous->location.col + tok2->previous->str.size()))
143  directive.str += ' ';
144  if (directive.str == "#" && tok2->str == "file")
145  directive.str += "include";
146  else
147  directive.str += tok2->str;
148  }
149  directives.push_back(directive);
150  }
151  }
152 }
153 
154 static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
155 {
156  return tok1 && tok2 && tok1->location.sameline(tok2->location);
157 }
158 
159 static std::string readcondition(const simplecpp::Token *iftok, const std::set<std::string> &defined, const std::set<std::string> &undefined)
160 {
161  const simplecpp::Token *cond = iftok->next;
162  if (!sameline(iftok,cond))
163  return "";
164 
165  const simplecpp::Token *next1 = cond->next;
166  const simplecpp::Token *next2 = next1 ? next1->next : nullptr;
167  const simplecpp::Token *next3 = next2 ? next2->next : nullptr;
168 
169  unsigned int len = 1;
170  if (sameline(iftok,next1))
171  len = 2;
172  if (sameline(iftok,next2))
173  len = 3;
174  if (sameline(iftok,next3))
175  len = 4;
176 
177  if (len == 1 && cond->str == "0")
178  return "0";
179 
180  if (len == 1 && cond->name) {
181  if (defined.find(cond->str) == defined.end())
182  return cond->str;
183  }
184 
185  if (len == 3 && cond->op == '(' && next1->name && next2->op == ')') {
186  if (defined.find(next1->str) == defined.end() && undefined.find(next1->str) == undefined.end())
187  return next1->str;
188  }
189 
190  if (len == 3 && cond->name && next1->str == "==" && next2->number) {
191  if (defined.find(cond->str) == defined.end())
192  return cond->str + '=' + cond->next->next->str;
193  }
194 
195  std::set<std::string> configset;
196  for (; sameline(iftok,cond); cond = cond->next) {
197  if (cond->op == '!')
198  break;
199  if (cond->str != "defined")
200  continue;
201  const simplecpp::Token *dtok = cond->next;
202  if (!dtok)
203  break;
204  if (dtok->op == '(')
205  dtok = dtok->next;
206  if (sameline(iftok,dtok) && dtok->name && defined.find(dtok->str) == defined.end() && undefined.find(dtok->str) == undefined.end())
207  configset.insert(dtok->str);
208  }
209  std::string cfg;
210  for (std::set<std::string>::const_iterator it = configset.begin(); it != configset.end(); ++it) {
211  if (!cfg.empty())
212  cfg += ';';
213  cfg += *it;
214  }
215  return cfg;
216 }
217 
218 static bool hasDefine(const std::string &userDefines, const std::string &cfg)
219 {
220  std::string::size_type pos = 0;
221  while (pos < userDefines.size()) {
222  pos = userDefines.find(cfg, pos);
223  if (pos == std::string::npos)
224  break;
225  const std::string::size_type pos2 = pos + cfg.size();
226  if ((pos == 0 || userDefines[pos-1U] == ';') && (pos2 == userDefines.size() || userDefines[pos2] == '='))
227  return true;
228  pos = pos2;
229  }
230  return false;
231 }
232 
233 static std::string cfg(const std::vector<std::string> &configs, const std::string &userDefines)
234 {
235  std::set<std::string> configs2(configs.begin(), configs.end());
236  std::string ret;
237  for (std::set<std::string>::const_iterator it = configs2.begin(); it != configs2.end(); ++it) {
238  if (it->empty())
239  continue;
240  if (*it == "0")
241  return "";
242  if (hasDefine(userDefines, *it))
243  continue;
244  if (!ret.empty())
245  ret += ';';
246  ret += *it;
247  }
248  return ret;
249 }
250 
251 static bool isUndefined(const std::string &cfg, const std::set<std::string> &undefined)
252 {
253  for (std::string::size_type pos1 = 0U; pos1 < cfg.size();) {
254  const std::string::size_type pos2 = cfg.find(";",pos1);
255  const std::string def = (pos2 == std::string::npos) ? cfg.substr(pos1) : cfg.substr(pos1, pos2 - pos1);
256 
257  std::string::size_type eq = def.find("=");
258  if (eq == std::string::npos && undefined.find(def) != undefined.end())
259  return true;
260  if (eq != std::string::npos && undefined.find(def.substr(0,eq)) != undefined.end() && def.substr(eq) != "=0")
261  return true;
262 
263  pos1 = (pos2 == std::string::npos) ? pos2 : pos2 + 1U;
264  }
265  return false;
266 }
267 
268 static bool getConfigsElseIsFalse(const std::vector<std::string> &configs_if, const std::string &userDefines)
269 {
270  for (unsigned int i = 0; i < configs_if.size(); ++i) {
271  if (hasDefine(userDefines, configs_if[i]))
272  return true;
273  }
274  return false;
275 }
276 
277 static const simplecpp::Token *gotoEndIf(const simplecpp::Token *cmdtok)
278 {
279  int level = 0;
280  while (nullptr != (cmdtok = cmdtok->next)) {
281  if (cmdtok->op == '#' && !sameline(cmdtok->previous,cmdtok) && sameline(cmdtok, cmdtok->next)) {
282  if (cmdtok->next->str.compare(0,2,"if")==0)
283  ++level;
284  else if (cmdtok->next->str == "endif") {
285  --level;
286  if (level < 0)
287  return cmdtok;
288  }
289  }
290  }
291  return nullptr;
292 }
293 
294 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)
295 {
296  std::vector<std::string> configs_if;
297  std::vector<std::string> configs_ifndef;
298 
299  for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
300  if (tok->op != '#' || sameline(tok->previous, tok))
301  continue;
302  const simplecpp::Token *cmdtok = tok->next;
303  if (!sameline(tok, cmdtok))
304  continue;
305  if (cmdtok->str == "ifdef" || cmdtok->str == "ifndef" || cmdtok->str == "if") {
306  std::string config;
307  if (cmdtok->str == "ifdef" || cmdtok->str == "ifndef") {
308  const simplecpp::Token *expr1 = cmdtok->next;
309  if (sameline(tok,expr1) && expr1->name && !sameline(tok,expr1->next))
310  config = expr1->str;
311  if (defined.find(config) != defined.end())
312  config.clear();
313  } else if (cmdtok->str == "if") {
314  config = readcondition(cmdtok, defined, undefined);
315  }
316 
317  // skip undefined configurations..
318  if (isUndefined(config, undefined))
319  config.clear();
320 
321  configs_if.push_back((cmdtok->str == "ifndef") ? std::string() : config);
322  configs_ifndef.push_back((cmdtok->str == "ifndef") ? config : std::string());
323  ret.insert(cfg(configs_if,userDefines));
324  } else if (cmdtok->str == "elif" || cmdtok->str == "else") {
325  if (getConfigsElseIsFalse(configs_if,userDefines)) {
326  tok = gotoEndIf(tok);
327  if (!tok)
328  break;
329  tok = tok->previous;
330  continue;
331  }
332  if (!configs_if.empty())
333  configs_if.pop_back();
334  if (cmdtok->str == "elif") {
335  std::string config = readcondition(cmdtok, defined, undefined);
336  if (isUndefined(config,undefined))
337  config.clear();
338  configs_if.push_back(config);
339  ret.insert(cfg(configs_if, userDefines));
340  } else if (!configs_ifndef.empty()) {
341  configs_if.push_back(configs_ifndef.back());
342  ret.insert(cfg(configs_if, userDefines));
343  }
344  } else if (cmdtok->str == "endif" && !sameline(tok, cmdtok->next)) {
345  if (!configs_if.empty())
346  configs_if.pop_back();
347  if (!configs_ifndef.empty())
348  configs_ifndef.pop_back();
349  } else if (cmdtok->str == "define" && sameline(tok, cmdtok->next) && cmdtok->next->name) {
350  defined.insert(cmdtok->next->str);
351  }
352  }
353 }
354 
355 
356 std::set<std::string> Preprocessor::getConfigs(const simplecpp::TokenList &tokens) const
357 {
358  std::set<std::string> ret;
359  ret.insert("");
360  if (!tokens.cfront())
361  return ret;
362 
363  std::set<std::string> defined;
364  defined.insert("__cplusplus");
365 
366  ::getConfigs(tokens, defined, _settings.userDefines, _settings.userUndefs, ret);
367 
368  for (std::map<std::string, simplecpp::TokenList*>::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it)
369  ::getConfigs(*(it->second), defined, _settings.userDefines, _settings.userUndefs, ret);
370 
371  return ret;
372 }
373 
374 
375 void Preprocessor::preprocess(std::istream &istr, std::map<std::string, std::string> &result, const std::string &filename, const std::list<std::string> &includePaths)
376 {
377  (void)includePaths;
378 
379  simplecpp::OutputList outputList;
380  std::vector<std::string> files;
381  const simplecpp::TokenList tokens1(istr, files, filename, &outputList);
382 
383 
384  const std::set<std::string> configs = getConfigs(tokens1);
385 
386  for (std::set<std::string>::const_iterator it = configs.begin(); it != configs.end(); ++it) {
387  if (_settings.userUndefs.find(*it) == _settings.userUndefs.end()) {
388  result[ *it ] = getcode(tokens1, *it, files, false);
389  }
390  }
391 }
392 
393 std::string Preprocessor::removeSpaceNearNL(const std::string &str)
394 {
395  std::string tmp;
396  char prev = '\n'; // treat start of file as newline
397  for (std::size_t i = 0; i < str.size(); i++) {
398  if (str[i] == ' ' &&
399  (prev == '\n' ||
400  i + 1 >= str.size() || // treat end of file as newline
401  str[i+1] == '\n'
402  )
403  ) {
404  // Ignore space that has new line in either side of it
405  } else {
406  tmp.append(1, str[i]);
407  prev = str[i];
408  }
409  }
410 
411  return tmp;
412 }
413 
414 void Preprocessor::preprocessWhitespaces(std::string &processedFile)
415 {
416  // Replace all tabs with spaces..
417  std::replace(processedFile.begin(), processedFile.end(), '\t', ' ');
418 
419  // Remove space characters that are after or before new line character
420  processedFile = removeSpaceNearNL(processedFile);
421 }
422 
423 void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list<std::string> &resultConfigurations, const std::string &filename, const std::list<std::string> &includePaths)
424 {
425  (void)includePaths;
426 
427  if (file0.empty())
428  file0 = filename;
429 
430  simplecpp::OutputList outputList;
431  std::vector<std::string> files;
432  const simplecpp::TokenList tokens1(srcCodeStream, files, filename, &outputList);
433 
434  const std::set<std::string> configs = getConfigs(tokens1);
435  for (std::set<std::string>::const_iterator it = configs.begin(); it != configs.end(); ++it)
436  resultConfigurations.push_back(*it);
437 
438  processedFile = tokens1.stringify();
439 }
440 
441 static void splitcfg(const std::string &cfg, std::list<std::string> &defines, const std::string &defaultValue)
442 {
443  for (std::string::size_type defineStartPos = 0U; defineStartPos < cfg.size();) {
444  const std::string::size_type defineEndPos = cfg.find(";", defineStartPos);
445  std::string def = (defineEndPos == std::string::npos) ? cfg.substr(defineStartPos) : cfg.substr(defineStartPos, defineEndPos - defineStartPos);
446  if (!defaultValue.empty() && def.find("=") == std::string::npos)
447  def += '=' + defaultValue;
448  defines.push_back(def);
449  if (defineEndPos == std::string::npos)
450  break;
451  defineStartPos = defineEndPos + 1U;
452  }
453 }
454 
455 static simplecpp::DUI createDUI(const Settings &_settings, const std::string &cfg, const std::string &filename)
456 {
457  simplecpp::DUI dui;
458 
459  splitcfg(_settings.userDefines, dui.defines, "1");
460  if (!cfg.empty())
461  splitcfg(cfg, dui.defines, "");
462 
463  for (std::vector<std::string>::const_iterator it = _settings.library.defines.begin(); it != _settings.library.defines.end(); ++it) {
464  if (it->compare(0,8,"#define ")!=0)
465  continue;
466  std::string s = it->substr(8);
467  std::string::size_type pos = s.find_first_of(" (");
468  if (pos == std::string::npos) {
469  dui.defines.push_back(s);
470  continue;
471  }
472  if (s[pos] == ' ') {
473  s[pos] = '=';
474  } else {
475  s[s.find(")")+1] = '=';
476  }
477  dui.defines.push_back(s);
478  }
479 
480  if (Path::isCPP(filename))
481  dui.defines.push_back("__cplusplus");
482 
483  dui.undefined = _settings.userUndefs; // -U
484  dui.includePaths = _settings.includePaths; // -I
485  dui.includes = _settings.userIncludes; // --include
486  return dui;
487 }
488 
489 static bool hasErrors(const simplecpp::OutputList &outputList)
490 {
491  for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) {
492  switch (it->type) {
493  case simplecpp::Output::ERROR:
494  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
495  case simplecpp::Output::SYNTAX_ERROR:
496  return true;
497  case simplecpp::Output::WARNING:
498  case simplecpp::Output::MISSING_HEADER:
499  break;
500  };
501  }
502  return false;
503 }
504 
505 
506 void Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector<std::string> &files)
507 {
508  const simplecpp::DUI dui = createDUI(_settings, "", files[0]);
509 
510  simplecpp::OutputList outputList;
511 
512  tokenlists = simplecpp::load(rawtokens, files, dui, &outputList);
513 }
514 
516 {
517  for (std::map<std::string, simplecpp::TokenList*>::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
518  if (it->second)
519  it->second->removeComments();
520  }
521 }
522 
523 void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const
524 {
525  tokens->sizeOfType["bool"] = _settings.sizeof_bool;
526  tokens->sizeOfType["short"] = _settings.sizeof_short;
527  tokens->sizeOfType["int"] = _settings.sizeof_int;
528  tokens->sizeOfType["long"] = _settings.sizeof_long;
529  tokens->sizeOfType["long long"] = _settings.sizeof_long_long;
530  tokens->sizeOfType["float"] = _settings.sizeof_float;
531  tokens->sizeOfType["double"] = _settings.sizeof_double;
532  tokens->sizeOfType["long double"] = _settings.sizeof_long_double;
533  tokens->sizeOfType["bool *"] = _settings.sizeof_pointer;
534  tokens->sizeOfType["short *"] = _settings.sizeof_pointer;
535  tokens->sizeOfType["int *"] = _settings.sizeof_pointer;
536  tokens->sizeOfType["long *"] = _settings.sizeof_pointer;
537  tokens->sizeOfType["long long *"] = _settings.sizeof_pointer;
538  tokens->sizeOfType["float *"] = _settings.sizeof_pointer;
539  tokens->sizeOfType["double *"] = _settings.sizeof_pointer;
540  tokens->sizeOfType["long double *"] = _settings.sizeof_pointer;
541 }
542 
543 std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, const bool writeLocations)
544 {
545  const std::string filename(files[0]);
546 
547  const simplecpp::DUI dui = createDUI(_settings, cfg, filename);
548 
549  simplecpp::OutputList outputList;
550  std::list<simplecpp::MacroUsage> macroUsage;
551  simplecpp::TokenList tokens2(files);
552  simplecpp::preprocess(tokens2, tokens1, files, tokenlists, dui, &outputList, &macroUsage);
553 
554  bool showerror = (!_settings.userDefines.empty() && !_settings.force);
555  reportOutput(outputList, showerror);
556  if (hasErrors(outputList))
557  return "";
558 
559  // ensure that guessed define macros without value are not used in the code
560  if (!validateCfg(cfg, macroUsage))
561  return "";
562 
563  // assembler code locations..
564  std::set<simplecpp::Location> assemblerLocations;
565  for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
566  const Directive &d1 = *dirIt;
567  if (d1.str.compare(0, 11, "#pragma asm") != 0)
568  continue;
569  std::list<Directive>::const_iterator dirIt2 = dirIt;
570  ++dirIt2;
571  if (dirIt2 == directives.end())
572  continue;
573 
574  const Directive &d2 = *dirIt2;
575  if (d2.str.compare(0,14,"#pragma endasm") != 0 || d1.file != d2.file)
576  continue;
577 
578  simplecpp::Location loc(files);
579  loc.fileIndex = ~0U;
580  loc.col = 0U;
581  for (unsigned int i = 0; i < files.size(); ++i) {
582  if (files[i] == d1.file) {
583  loc.fileIndex = i;
584  break;
585  }
586  }
587 
588  for (unsigned int linenr = d1.linenr + 1U; linenr < d2.linenr; linenr++) {
589  loc.line = linenr;
590  assemblerLocations.insert(loc);
591  }
592  }
593 
594  unsigned int prevfile = 0;
595  unsigned int line = 1;
596  std::ostringstream ret;
597  for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) {
598  if (writeLocations && tok->location.fileIndex != prevfile) {
599  ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
600  prevfile = tok->location.fileIndex;
601  line = tok->location.line;
602  }
603 
604  if (tok->previous && line == tok->location.line)
605  ret << ' ';
606  bool newline = false;
607  while (tok->location.line > line) {
608  ret << '\n';
609  line++;
610  newline = true;
611  }
612  if (newline) {
613  simplecpp::Location loc = tok->location;
614  loc.col = 0U;
615  if (assemblerLocations.find(loc) != assemblerLocations.end()) {
616  ret << "asm();";
617  while (assemblerLocations.find(loc) != assemblerLocations.end()) {
618  loc.line++;
619  }
620  while (tok && tok->location.line < loc.line)
621  tok = tok->next;
622  if (!tok)
623  break;
624  while (line < tok->location.line) {
625  ret << '\n';
626  ++line;
627  }
628  }
629  }
630  if (!tok->macro.empty())
632  ret << tok->str;
633  }
634 
635  return ret.str();
636 }
637 
638 std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename)
639 {
640  simplecpp::OutputList outputList;
641  std::vector<std::string> files;
642 
643  std::istringstream istr(filedata);
644  simplecpp::TokenList tokens1(istr, files, Path::simplifyPath(filename), &outputList);
645  inlineSuppressions(tokens1);
646  tokens1.removeComments();
647  removeComments();
648  setDirectives(tokens1);
649 
650  reportOutput(outputList, true);
651 
652  if (hasErrors(outputList))
653  return "";
654 
655  return getcode(tokens1, cfg, files, filedata.find("#file") != std::string::npos);
656 }
657 
658 void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror)
659 {
660  for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) {
661  switch (it->type) {
662  case simplecpp::Output::ERROR:
663  if (it->msg.compare(0,6,"#error")!=0 || showerror)
664  error(it->location.file(), it->location.line, it->msg);
665  break;
666  case simplecpp::Output::WARNING:
667  break;
668  case simplecpp::Output::MISSING_HEADER: {
669  const std::string::size_type pos1 = it->msg.find_first_of("<\"");
670  const std::string::size_type pos2 = it->msg.find_first_of(">\"", pos1 + 1U);
671  if (pos1 < pos2 && pos2 != std::string::npos)
672  missingInclude(it->location.file(), it->location.line, it->msg.substr(pos1+1, pos2-pos1-1), it->msg[pos1] == '\"' ? UserHeader : SystemHeader);
673  }
674  break;
675  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
676  case simplecpp::Output::SYNTAX_ERROR:
677  error(it->location.file(), it->location.line, it->msg);
678  break;
679  };
680  }
681 }
682 
683 void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg)
684 {
685  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
686  if (!filename.empty()) {
687  ErrorLogger::ErrorMessage::FileLocation loc(filename, linenr);
688  locationList.push_back(loc);
689  }
691  file0,
693  msg,
694  "preprocessorErrorDirective",
695  false));
696 }
697 
698 // Report that include is missing
699 void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
700 {
701  const std::string fname = Path::fromNativeSeparators(filename);
702  if (_settings.nomsg.isSuppressed("missingInclude", fname, linenr))
703  return;
704  if (headerType == SystemHeader && _settings.nomsg.isSuppressed("missingIncludeSystem", fname, linenr))
705  return;
706 
707  if (headerType == SystemHeader)
709  else
710  missingIncludeFlag = true;
712 
713  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
714  if (!filename.empty()) {
716  loc.line = linenr;
717  loc.setfile(Path::toNativeSeparators(filename));
718  locationList.push_back(loc);
719  }
721  (headerType==SystemHeader) ?
722  "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." :
723  "Include file: \"" + header + "\" not found.",
724  (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude",
725  false);
726  _errorLogger->reportInfo(errmsg);
727  }
728 }
729 
730 bool Preprocessor::validateCfg(const std::string &cfg, const std::list<simplecpp::MacroUsage> &macroUsageList)
731 {
732  bool ret = true;
733  std::list<std::string> defines;
734  splitcfg(cfg, defines, std::string());
735  for (std::list<std::string>::const_iterator defineIt = defines.begin(); defineIt != defines.end(); ++defineIt) {
736  if (defineIt->find("=") != std::string::npos)
737  continue;
738  const std::string macroName(defineIt->substr(0, defineIt->find("(")));
739  for (std::list<simplecpp::MacroUsage>::const_iterator usageIt = macroUsageList.begin(); usageIt != macroUsageList.end(); ++usageIt) {
740  const simplecpp::MacroUsage &mu = *usageIt;
741  if (mu.macroName != macroName)
742  continue;
743  bool directiveLocation = false;
744  for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
745  if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) {
746  directiveLocation = true;
747  break;
748  }
749  }
750  if (!directiveLocation) {
751  if (_settings.isEnabled("information"))
752  validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName);
753  ret = false;
754  }
755  }
756  }
757 
758  return ret;
759 }
760 
761 void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro)
762 {
763  const std::string id = "ConfigurationNotChecked";
764  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
766  locationList.push_back(loc);
767  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);
768  _errorLogger->reportInfo(errmsg);
769 }
770 
771 void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
772 {
773  Settings settings2(*settings);
774  Preprocessor preprocessor(settings2, errorLogger);
775  settings2.checkConfiguration=true;
776  preprocessor.missingInclude("", 1, "", UserHeader);
777  preprocessor.missingInclude("", 1, "", SystemHeader);
778  preprocessor.validateCfgError("", 1, "X", "X");
779  preprocessor.error("", 1, "#error message"); // #error ..
780 }
781 
782 void Preprocessor::dump(std::ostream &out) const
783 {
784  // Create a xml directive dump.
785  // The idea is not that this will be readable for humans. It's a
786  // data dump that 3rd party tools could load and get useful info from.
787  std::list<Directive>::const_iterator it;
788 
789  out << " <directivelist>" << std::endl;
790 
791  for (it = directives.begin(); it != directives.end(); ++it) {
792  out << " <directive "
793  << "file=\"" << it->file << "\" "
794  << "linenr=\"" << it->linenr << "\" "
795  // str might contain characters such as '"', '<' or '>' which
796  // could result in invalid XML, so run it through toxml().
797  << "str=\"" << ErrorLogger::toxml(it->str) << "\"/>" << std::endl;
798  }
799  out << " </directivelist>" << std::endl;
800 }
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:168
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:47
std::list< std::string > includePaths
List of include paths, e.g.
Definition: settings.h:147
HeaderTypes
Include file types.
Definition: preprocessor.h:74
std::string str
the actual directive text
Definition: preprocessor.h:54
Programming error.
Definition: errorlogger.h:72
static bool hasDefine(const std::string &userDefines, const std::string &cfg)
unsigned int sizeof_long
Definition: platform.h:50
bool isEnabled(T &&str) const
Returns true if given id is in the list of enabled extra checks (–enable)
Definition: settings.h:166
void removeComments()
virtual void reportInfo(const ErrorLogger::ErrorMessage &msg)
Output information messages.
Definition: errorlogger.h:316
bool relativePaths
Use relative paths in output.
Definition: settings.h:113
static std::string fromNativeSeparators(std::string path)
Convert path to use internal path separators.
Definition: path.cpp:53
Checking information.
Definition: errorlogger.h:106
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:48
Settings & _settings
Definition: preprocessor.h:193
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:51
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:61
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.
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:52
std::list< Directive > directives
list of all directives met while preprocessing file
Definition: preprocessor.h:197
bool checkConfiguration
Is the 'configuration checking' wanted?
Definition: settings.h:239
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:162
unsigned int sizeof_pointer
Definition: platform.h:57
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:193
std::set< std::string > userUndefs
undefines given by the user
Definition: settings.h:202
static void preprocessWhitespaces(std::string &processedFile)
preprocess all whitespaces
unsigned int sizeof_double
Definition: platform.h:53
std::vector< std::string > defines
Definition: library.h:352
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:208
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:199
Library library
Library (–library)
Definition: settings.h:215
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:199
Preprocessor(Settings &settings, ErrorLogger *errorLogger=nullptr)
void reportOutput(const simplecpp::OutputList &outputList, bool showerror)
void dump(std::ostream &out) const
dump all directives present in source file
unsigned int sizeof_int
Definition: platform.h:49
void setPlatformInfo(simplecpp::TokenList *tokens) const
ErrorLogger * _errorLogger
Definition: preprocessor.h:194
static bool hasErrors(const simplecpp::OutputList &outputList)
static void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
File name and line number.
Definition: errorlogger.h:175
unsigned int sizeof_long_double
Definition: platform.h:54
bool inlineSuppressions
Is –inline-suppr given?
Definition: settings.h:94
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:202
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:110
std::vector< std::string > basePaths
Paths used as base for conversion to relative paths.
Definition: settings.h:116
void setfile(const std::string &file)
Set the filename.
std::list< std::string > userIncludes
forced includes given by the user
Definition: settings.h:205
static std::string toNativeSeparators(std::string path)
Convert path to use native separators.
Definition: path.cpp:40
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)