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  case simplecpp::Output::PORTABILITY_BACKSLASH:
500  break;
501  };
502  }
503  return false;
504 }
505 
506 
507 void Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector<std::string> &files)
508 {
509  const simplecpp::DUI dui = createDUI(_settings, "", files[0]);
510 
511  simplecpp::OutputList outputList;
512 
513  tokenlists = simplecpp::load(rawtokens, files, dui, &outputList);
514 }
515 
517 {
518  for (std::map<std::string, simplecpp::TokenList*>::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) {
519  if (it->second)
520  it->second->removeComments();
521  }
522 }
523 
524 void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const
525 {
526  tokens->sizeOfType["bool"] = _settings.sizeof_bool;
527  tokens->sizeOfType["short"] = _settings.sizeof_short;
528  tokens->sizeOfType["int"] = _settings.sizeof_int;
529  tokens->sizeOfType["long"] = _settings.sizeof_long;
530  tokens->sizeOfType["long long"] = _settings.sizeof_long_long;
531  tokens->sizeOfType["float"] = _settings.sizeof_float;
532  tokens->sizeOfType["double"] = _settings.sizeof_double;
533  tokens->sizeOfType["long double"] = _settings.sizeof_long_double;
534  tokens->sizeOfType["bool *"] = _settings.sizeof_pointer;
535  tokens->sizeOfType["short *"] = _settings.sizeof_pointer;
536  tokens->sizeOfType["int *"] = _settings.sizeof_pointer;
537  tokens->sizeOfType["long *"] = _settings.sizeof_pointer;
538  tokens->sizeOfType["long long *"] = _settings.sizeof_pointer;
539  tokens->sizeOfType["float *"] = _settings.sizeof_pointer;
540  tokens->sizeOfType["double *"] = _settings.sizeof_pointer;
541  tokens->sizeOfType["long double *"] = _settings.sizeof_pointer;
542 }
543 
544 std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, const bool writeLocations)
545 {
546  const std::string filename(files[0]);
547 
548  const simplecpp::DUI dui = createDUI(_settings, cfg, filename);
549 
550  simplecpp::OutputList outputList;
551  std::list<simplecpp::MacroUsage> macroUsage;
552  simplecpp::TokenList tokens2(files);
553  simplecpp::preprocess(tokens2, tokens1, files, tokenlists, dui, &outputList, &macroUsage);
554 
555  bool showerror = (!_settings.userDefines.empty() && !_settings.force);
556  reportOutput(outputList, showerror);
557  if (hasErrors(outputList))
558  return "";
559 
560  // ensure that guessed define macros without value are not used in the code
561  if (!validateCfg(cfg, macroUsage))
562  return "";
563 
564  // assembler code locations..
565  std::set<simplecpp::Location> assemblerLocations;
566  for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
567  const Directive &d1 = *dirIt;
568  if (d1.str.compare(0, 11, "#pragma asm") != 0)
569  continue;
570  std::list<Directive>::const_iterator dirIt2 = dirIt;
571  ++dirIt2;
572  if (dirIt2 == directives.end())
573  continue;
574 
575  const Directive &d2 = *dirIt2;
576  if (d2.str.compare(0,14,"#pragma endasm") != 0 || d1.file != d2.file)
577  continue;
578 
579  simplecpp::Location loc(files);
580  loc.fileIndex = ~0U;
581  loc.col = 0U;
582  for (unsigned int i = 0; i < files.size(); ++i) {
583  if (files[i] == d1.file) {
584  loc.fileIndex = i;
585  break;
586  }
587  }
588 
589  for (unsigned int linenr = d1.linenr + 1U; linenr < d2.linenr; linenr++) {
590  loc.line = linenr;
591  assemblerLocations.insert(loc);
592  }
593  }
594 
595  unsigned int prevfile = 0;
596  unsigned int line = 1;
597  std::ostringstream ret;
598  for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) {
599  if (writeLocations && tok->location.fileIndex != prevfile) {
600  ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
601  prevfile = tok->location.fileIndex;
602  line = tok->location.line;
603  }
604 
605  if (tok->previous && line == tok->location.line)
606  ret << ' ';
607  bool newline = false;
608  while (tok->location.line > line) {
609  ret << '\n';
610  line++;
611  newline = true;
612  }
613  if (newline) {
614  simplecpp::Location loc = tok->location;
615  loc.col = 0U;
616  if (assemblerLocations.find(loc) != assemblerLocations.end()) {
617  ret << "asm();";
618  while (assemblerLocations.find(loc) != assemblerLocations.end()) {
619  loc.line++;
620  }
621  while (tok && tok->location.line < loc.line)
622  tok = tok->next;
623  if (!tok)
624  break;
625  while (line < tok->location.line) {
626  ret << '\n';
627  ++line;
628  }
629  }
630  }
631  if (!tok->macro.empty())
633  ret << tok->str;
634  }
635 
636  return ret.str();
637 }
638 
639 std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename)
640 {
641  simplecpp::OutputList outputList;
642  std::vector<std::string> files;
643 
644  std::istringstream istr(filedata);
645  simplecpp::TokenList tokens1(istr, files, Path::simplifyPath(filename), &outputList);
646  inlineSuppressions(tokens1);
647  tokens1.removeComments();
648  removeComments();
649  setDirectives(tokens1);
650 
651  reportOutput(outputList, true);
652 
653  if (hasErrors(outputList))
654  return "";
655 
656  return getcode(tokens1, cfg, files, filedata.find("#file") != std::string::npos);
657 }
658 
659 void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror)
660 {
661  for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) {
662  switch (it->type) {
663  case simplecpp::Output::ERROR:
664  if (it->msg.compare(0,6,"#error")!=0 || showerror)
665  error(it->location.file(), it->location.line, it->msg);
666  break;
667  case simplecpp::Output::WARNING:
668  case simplecpp::Output::PORTABILITY_BACKSLASH:
669  break;
670  case simplecpp::Output::MISSING_HEADER: {
671  const std::string::size_type pos1 = it->msg.find_first_of("<\"");
672  const std::string::size_type pos2 = it->msg.find_first_of(">\"", pos1 + 1U);
673  if (pos1 < pos2 && pos2 != std::string::npos)
674  missingInclude(it->location.file(), it->location.line, it->msg.substr(pos1+1, pos2-pos1-1), it->msg[pos1] == '\"' ? UserHeader : SystemHeader);
675  }
676  break;
677  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
678  case simplecpp::Output::SYNTAX_ERROR:
679  error(it->location.file(), it->location.line, it->msg);
680  break;
681  };
682  }
683 }
684 
685 void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg)
686 {
687  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
688  if (!filename.empty()) {
689  ErrorLogger::ErrorMessage::FileLocation loc(filename, linenr);
690  locationList.push_back(loc);
691  }
693  file0,
695  msg,
696  "preprocessorErrorDirective",
697  false));
698 }
699 
700 // Report that include is missing
701 void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
702 {
703  const std::string fname = Path::fromNativeSeparators(filename);
704  if (_settings.nomsg.isSuppressed("missingInclude", fname, linenr))
705  return;
706  if (headerType == SystemHeader && _settings.nomsg.isSuppressed("missingIncludeSystem", fname, linenr))
707  return;
708 
709  if (headerType == SystemHeader)
711  else
712  missingIncludeFlag = true;
714 
715  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
716  if (!filename.empty()) {
718  loc.line = linenr;
719  loc.setfile(Path::toNativeSeparators(filename));
720  locationList.push_back(loc);
721  }
723  (headerType==SystemHeader) ?
724  "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." :
725  "Include file: \"" + header + "\" not found.",
726  (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude",
727  false);
728  _errorLogger->reportInfo(errmsg);
729  }
730 }
731 
732 bool Preprocessor::validateCfg(const std::string &cfg, const std::list<simplecpp::MacroUsage> &macroUsageList)
733 {
734  bool ret = true;
735  std::list<std::string> defines;
736  splitcfg(cfg, defines, std::string());
737  for (std::list<std::string>::const_iterator defineIt = defines.begin(); defineIt != defines.end(); ++defineIt) {
738  if (defineIt->find("=") != std::string::npos)
739  continue;
740  const std::string macroName(defineIt->substr(0, defineIt->find("(")));
741  for (std::list<simplecpp::MacroUsage>::const_iterator usageIt = macroUsageList.begin(); usageIt != macroUsageList.end(); ++usageIt) {
742  const simplecpp::MacroUsage &mu = *usageIt;
743  if (mu.macroName != macroName)
744  continue;
745  bool directiveLocation = false;
746  for (std::list<Directive>::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) {
747  if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) {
748  directiveLocation = true;
749  break;
750  }
751  }
752  if (!directiveLocation) {
753  if (_settings.isEnabled("information"))
754  validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName);
755  ret = false;
756  }
757  }
758  }
759 
760  return ret;
761 }
762 
763 void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro)
764 {
765  const std::string id = "ConfigurationNotChecked";
766  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
768  locationList.push_back(loc);
769  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);
770  _errorLogger->reportInfo(errmsg);
771 }
772 
773 void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
774 {
775  Settings settings2(*settings);
776  Preprocessor preprocessor(settings2, errorLogger);
777  settings2.checkConfiguration=true;
778  preprocessor.missingInclude("", 1, "", UserHeader);
779  preprocessor.missingInclude("", 1, "", SystemHeader);
780  preprocessor.validateCfgError("", 1, "X", "X");
781  preprocessor.error("", 1, "#error message"); // #error ..
782 }
783 
784 void Preprocessor::dump(std::ostream &out) const
785 {
786  // Create a xml directive dump.
787  // The idea is not that this will be readable for humans. It's a
788  // data dump that 3rd party tools could load and get useful info from.
789  std::list<Directive>::const_iterator it;
790 
791  out << " <directivelist>" << std::endl;
792 
793  for (it = directives.begin(); it != directives.end(); ++it) {
794  out << " <directive "
795  << "file=\"" << it->file << "\" "
796  << "linenr=\"" << it->linenr << "\" "
797  // str might contain characters such as '"', '<' or '>' which
798  // could result in invalid XML, so run it through toxml().
799  << "str=\"" << ErrorLogger::toxml(it->str) << "\"/>" << std::endl;
800  }
801  out << " </directivelist>" << std::endl;
802 }
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)