Cppcheck
preprocessor.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 
20 #include "preprocessor.h"
21 
22 #include "errorlogger.h"
23 #include "errortypes.h"
24 #include "library.h"
25 #include "path.h"
26 #include "platform.h"
27 #include "settings.h"
28 #include "standards.h"
29 #include "suppressions.h"
30 #include "utils.h"
31 
32 #include <algorithm>
33 #include <array>
34 #include <cstddef>
35 #include <iterator>
36 #include <sstream>
37 #include <utility>
38 
39 #include <simplecpp.h>
40 
41 static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
42 {
43  return tok1 && tok2 && tok1->location.sameline(tok2->location);
44 }
45 
46 Directive::Directive(std::string _file, const int _linenr, const std::string &_str) :
47  file(std::move(_file)),
48  linenr(_linenr),
49  str(trim(_str))
50 {}
51 
52 char Preprocessor::macroChar = char(1);
53 
54 Preprocessor::Preprocessor(const Settings& settings, ErrorLogger &errorLogger) : mSettings(settings), mErrorLogger(errorLogger)
55 {}
56 
58 {
59  for (const std::pair<const std::string, simplecpp::TokenList*>& tokenList : mTokenLists)
60  delete tokenList.second;
61 }
62 
63 namespace {
64  struct BadInlineSuppression {
65  BadInlineSuppression(std::string file, const int line, std::string msg) : file(std::move(file)), line(line), errmsg(std::move(msg)) {}
66  std::string file;
67  int line;
68  std::string errmsg;
69  };
70 }
71 
72 static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std::list<SuppressionList::Suppression> &inlineSuppressions, std::list<BadInlineSuppression> &bad)
73 {
74  const std::string cppchecksuppress("cppcheck-suppress");
75 
76  const std::string &comment = tok->str();
77  if (comment.size() < cppchecksuppress.size())
78  return false;
79  const std::string::size_type pos1 = comment.find_first_not_of("/* \t");
80  if (pos1 == std::string::npos)
81  return false;
82  if (pos1 + cppchecksuppress.size() >= comment.size())
83  return false;
84  if (comment.substr(pos1, cppchecksuppress.size()) != cppchecksuppress)
85  return false;
86 
87  // check if it has a prefix
88  const std::string::size_type posEndComment = comment.find_first_of(" [", pos1+cppchecksuppress.size());
89 
90  // skip spaces after "cppcheck-suppress" and its possible prefix
91  const std::string::size_type pos2 = comment.find_first_not_of(' ', posEndComment);
92  if (pos2 == std::string::npos)
93  return false;
94 
96 
97  // determine prefix if specified
98  if (posEndComment >= (pos1 + cppchecksuppress.size() + 1)) {
99  if (comment.at(pos1 + cppchecksuppress.size()) != '-')
100  return false;
101 
102  const unsigned int argumentLength =
103  posEndComment - (pos1 + cppchecksuppress.size() + 1);
104 
105  const std::string suppressTypeString =
106  comment.substr(pos1 + cppchecksuppress.size() + 1, argumentLength);
107 
108  if ("file" == suppressTypeString)
109  errorType = SuppressionList::Type::file;
110  else if ("begin" == suppressTypeString)
112  else if ("end" == suppressTypeString)
114  else if ("macro" == suppressTypeString)
115  errorType = SuppressionList::Type::macro;
116  else
117  return false;
118  }
119 
120  if (comment[pos2] == '[') {
121  // multi suppress format
122  std::string errmsg;
123  std::vector<SuppressionList::Suppression> suppressions = SuppressionList::parseMultiSuppressComment(comment, &errmsg);
124 
125  for (SuppressionList::Suppression &s : suppressions) {
126  s.type = errorType;
127  s.lineNumber = tok->location.line;
128  }
129 
130  if (!errmsg.empty())
131  bad.emplace_back(tok->location.file(), tok->location.line, std::move(errmsg));
132 
133  std::copy_if(suppressions.cbegin(), suppressions.cend(), std::back_inserter(inlineSuppressions), [](const SuppressionList::Suppression& s) {
134  return !s.errorId.empty();
135  });
136  } else {
137  //single suppress format
138  std::string errmsg;
140  if (!s.parseComment(comment, &errmsg))
141  return false;
142 
143  s.type = errorType;
144  s.lineNumber = tok->location.line;
145 
146  if (!s.errorId.empty())
147  inlineSuppressions.push_back(std::move(s));
148 
149  if (!errmsg.empty())
150  bad.emplace_back(tok->location.file(), tok->location.line, std::move(errmsg));
151  }
152 
153  return true;
154 }
155 
156 static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Settings &settings, SuppressionList &suppressions, std::list<BadInlineSuppression> &bad)
157 {
158  std::list<SuppressionList::Suppression> inlineSuppressionsBlockBegin;
159 
160  bool onlyComments = true;
161 
162  for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
163  if (!tok->comment) {
164  onlyComments = false;
165  continue;
166  }
167 
168  std::list<SuppressionList::Suppression> inlineSuppressions;
169  if (!parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad))
170  continue;
171 
172  if (!sameline(tok->previous, tok)) {
173  // find code after comment..
174  if (tok->next) {
175  tok = tok->next;
176 
177  while (tok->comment) {
178  parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad);
179  if (tok->next) {
180  tok = tok->next;
181  } else {
182  break;
183  }
184  }
185  }
186  }
187 
188  if (inlineSuppressions.empty())
189  continue;
190 
191  // It should never happen
192  if (!tok)
193  continue;
194 
195  // Relative filename
196  std::string relativeFilename(tok->location.file());
197  if (settings.relativePaths) {
198  for (const std::string & basePath : settings.basePaths) {
199  const std::string bp = basePath + "/";
200  if (relativeFilename.compare(0,bp.size(),bp)==0) {
201  relativeFilename = relativeFilename.substr(bp.size());
202  }
203  }
204  }
205  relativeFilename = Path::simplifyPath(relativeFilename);
206 
207  // Macro name
208  std::string macroName;
209  if (tok->str() == "#" && tok->next && tok->next->str() == "define") {
210  const simplecpp::Token *macroNameTok = tok->next->next;
211  if (sameline(tok, macroNameTok) && macroNameTok->name) {
212  macroName = macroNameTok->str();
213  }
214  }
215 
216  // Add the suppressions.
217  for (SuppressionList::Suppression &suppr : inlineSuppressions) {
218  suppr.fileName = relativeFilename;
219 
220  if (SuppressionList::Type::blockBegin == suppr.type)
221  {
222  inlineSuppressionsBlockBegin.push_back(std::move(suppr));
223  } else if (SuppressionList::Type::blockEnd == suppr.type) {
224  bool throwError = true;
225 
226  if (!inlineSuppressionsBlockBegin.empty()) {
227  const SuppressionList::Suppression lastBeginSuppression = inlineSuppressionsBlockBegin.back();
228 
229  auto supprBegin = inlineSuppressionsBlockBegin.begin();
230  while (supprBegin != inlineSuppressionsBlockBegin.end())
231  {
232  if (lastBeginSuppression.lineNumber != supprBegin->lineNumber) {
233  ++supprBegin;
234  continue;
235  }
236 
237  if (suppr.symbolName == supprBegin->symbolName && suppr.lineNumber > supprBegin->lineNumber) {
238  suppr.lineBegin = supprBegin->lineNumber;
239  suppr.lineEnd = suppr.lineNumber;
240  suppr.lineNumber = supprBegin->lineNumber;
241  suppr.type = SuppressionList::Type::block;
242  inlineSuppressionsBlockBegin.erase(supprBegin);
243  suppressions.addSuppression(std::move(suppr));
244  throwError = false;
245  break;
246  }
247  ++supprBegin;
248  }
249  }
250 
251  if (throwError) {
252  // NOLINTNEXTLINE(bugprone-use-after-move) - moved only when thrownError is false
253  bad.emplace_back(suppr.fileName, suppr.lineNumber, "Suppress End: No matching begin");
254  }
255  } else if (SuppressionList::Type::unique == suppr.type || suppr.type == SuppressionList::Type::macro) {
256  // special handling when suppressing { warnings for backwards compatibility
257  const bool thisAndNextLine = tok->previous &&
258  tok->previous->previous &&
259  tok->next &&
260  !sameline(tok->previous->previous, tok->previous) &&
261  tok->location.line + 1 == tok->next->location.line &&
262  tok->location.fileIndex == tok->next->location.fileIndex &&
263  tok->previous->str() == "{";
264 
265  suppr.thisAndNextLine = thisAndNextLine;
266  suppr.lineNumber = tok->location.line;
267  suppr.macroName = macroName;
268  suppressions.addSuppression(std::move(suppr));
269  } else if (SuppressionList::Type::file == suppr.type) {
270  if (onlyComments)
271  suppressions.addSuppression(std::move(suppr));
272  else
273  bad.emplace_back(suppr.fileName, suppr.lineNumber, "File suppression should be at the top of the file");
274  }
275  }
276  }
277 
278  for (const SuppressionList::Suppression & suppr: inlineSuppressionsBlockBegin)
279  // cppcheck-suppress useStlAlgorithm
280  bad.emplace_back(suppr.fileName, suppr.lineNumber, "Suppress Begin: No matching end");
281 }
282 
283 void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, SuppressionList &suppressions)
284 {
286  return;
287  std::list<BadInlineSuppression> err;
288  ::addInlineSuppressions(tokens, mSettings, suppressions, err);
289  for (std::map<std::string,simplecpp::TokenList*>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) {
290  if (it->second)
291  ::addInlineSuppressions(*it->second, mSettings, suppressions, err);
292  }
293  for (const BadInlineSuppression &bad : err) {
294  error(bad.file, bad.line, bad.errmsg);
295  }
296 }
297 
298 std::list<Directive> Preprocessor::createDirectives(const simplecpp::TokenList &tokens) const
299 {
300  // directive list..
301  std::list<Directive> directives;
302 
303  std::vector<const simplecpp::TokenList *> list;
304  list.reserve(1U + mTokenLists.size());
305  list.push_back(&tokens);
306  for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) {
307  list.push_back(it->second);
308  }
309 
310  for (const simplecpp::TokenList *tokenList : list) {
311  for (const simplecpp::Token *tok = tokenList->cfront(); tok; tok = tok->next) {
312  if ((tok->op != '#') || (tok->previous && tok->previous->location.line == tok->location.line))
313  continue;
314  if (tok->next && tok->next->str() == "endfile")
315  continue;
316  Directive directive(tok->location.file(), tok->location.line, emptyString);
317  for (const simplecpp::Token *tok2 = tok; tok2 && tok2->location.line == directive.linenr; tok2 = tok2->next) {
318  if (tok2->comment)
319  continue;
320  if (!directive.str.empty() && (tok2->location.col > tok2->previous->location.col + tok2->previous->str().size()))
321  directive.str += ' ';
322  if (directive.str == "#" && tok2->str() == "file")
323  directive.str += "include";
324  else
325  directive.str += tok2->str();
326  }
327  directives.push_back(std::move(directive));
328  }
329  }
330 
331  return directives;
332 }
333 
334 static std::string readcondition(const simplecpp::Token *iftok, const std::set<std::string> &defined, const std::set<std::string> &undefined)
335 {
336  const simplecpp::Token *cond = iftok->next;
337  if (!sameline(iftok,cond))
338  return "";
339 
340  const simplecpp::Token *next1 = cond->next;
341  const simplecpp::Token *next2 = next1 ? next1->next : nullptr;
342  const simplecpp::Token *next3 = next2 ? next2->next : nullptr;
343 
344  unsigned int len = 1;
345  if (sameline(iftok,next1))
346  len = 2;
347  if (sameline(iftok,next2))
348  len = 3;
349  if (sameline(iftok,next3))
350  len = 4;
351 
352  if (len == 1 && cond->str() == "0")
353  return "0";
354 
355  if (len == 1 && cond->name) {
356  if (defined.find(cond->str()) == defined.end())
357  return cond->str();
358  }
359 
360  if (len == 2 && cond->op == '!' && next1->name) {
361  if (defined.find(next1->str()) == defined.end())
362  return next1->str() + "=0";
363  }
364 
365  if (len == 3 && cond->op == '(' && next1->name && next2->op == ')') {
366  if (defined.find(next1->str()) == defined.end() && undefined.find(next1->str()) == undefined.end())
367  return next1->str();
368  }
369 
370  if (len == 3 && cond->name && next1->str() == "==" && next2->number) {
371  if (defined.find(cond->str()) == defined.end())
372  return cond->str() + '=' + cond->next->next->str();
373  }
374 
375  std::set<std::string> configset;
376  for (; sameline(iftok,cond); cond = cond->next) {
377  if (cond->op == '!') {
378  if (!sameline(iftok,cond->next) || !cond->next->name)
379  break;
380  if (cond->next->str() == "defined")
381  continue;
382  configset.insert(cond->next->str() + "=0");
383  continue;
384  }
385  if (cond->str() != "defined")
386  continue;
387  const simplecpp::Token *dtok = cond->next;
388  if (!dtok)
389  break;
390  if (dtok->op == '(')
391  dtok = dtok->next;
392  if (sameline(iftok,dtok) && dtok->name && defined.find(dtok->str()) == defined.end() && undefined.find(dtok->str()) == undefined.end())
393  configset.insert(dtok->str());
394  }
395  std::string cfgStr;
396  for (const std::string &s : configset) {
397  if (!cfgStr.empty())
398  cfgStr += ';';
399  cfgStr += s;
400  }
401  return cfgStr;
402 }
403 
404 static bool hasDefine(const std::string &userDefines, const std::string &cfg)
405 {
406  if (cfg.empty()) {
407  return false;
408  }
409 
410  std::string::size_type pos = 0;
411  while (pos < userDefines.size()) {
412  pos = userDefines.find(cfg, pos);
413  if (pos == std::string::npos)
414  break;
415  const std::string::size_type pos2 = pos + cfg.size();
416  if ((pos == 0 || userDefines[pos-1U] == ';') && (pos2 == userDefines.size() || userDefines[pos2] == '='))
417  return true;
418  pos = pos2;
419  }
420  return false;
421 }
422 
423 static std::string cfg(const std::vector<std::string> &configs, const std::string &userDefines)
424 {
425  std::set<std::string> configs2(configs.cbegin(), configs.cend());
426  std::string ret;
427  for (const std::string &c : configs2) {
428  if (c.empty())
429  continue;
430  if (c == "0")
431  return "";
432  if (hasDefine(userDefines, c))
433  continue;
434  if (!ret.empty())
435  ret += ';';
436  ret += c;
437  }
438  return ret;
439 }
440 
441 static bool isUndefined(const std::string &cfg, const std::set<std::string> &undefined)
442 {
443  for (std::string::size_type pos1 = 0U; pos1 < cfg.size();) {
444  const std::string::size_type pos2 = cfg.find(';',pos1);
445  const std::string def = (pos2 == std::string::npos) ? cfg.substr(pos1) : cfg.substr(pos1, pos2 - pos1);
446 
447  const std::string::size_type eq = def.find('=');
448  if (eq == std::string::npos && undefined.find(def) != undefined.end())
449  return true;
450  if (eq != std::string::npos && undefined.find(def.substr(0,eq)) != undefined.end() && def.substr(eq) != "=0")
451  return true;
452 
453  pos1 = (pos2 == std::string::npos) ? pos2 : pos2 + 1U;
454  }
455  return false;
456 }
457 
458 static bool getConfigsElseIsFalse(const std::vector<std::string> &configs_if, const std::string &userDefines)
459 {
460  return std::any_of(configs_if.cbegin(), configs_if.cend(),
461  [=](const std::string &cfg) {
462  return hasDefine(userDefines, cfg);
463  });
464 }
465 
466 static const simplecpp::Token *gotoEndIf(const simplecpp::Token *cmdtok)
467 {
468  int level = 0;
469  while (nullptr != (cmdtok = cmdtok->next)) {
470  if (cmdtok->op == '#' && !sameline(cmdtok->previous,cmdtok) && sameline(cmdtok, cmdtok->next)) {
471  if (startsWith(cmdtok->next->str(),"if"))
472  ++level;
473  else if (cmdtok->next->str() == "endif") {
474  --level;
475  if (level < 0)
476  return cmdtok;
477  }
478  }
479  }
480  return nullptr;
481 }
482 
483 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)
484 {
485  std::vector<std::string> configs_if;
486  std::vector<std::string> configs_ifndef;
487  std::string elseError;
488 
489  for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
490  if (tok->op != '#' || sameline(tok->previous, tok))
491  continue;
492  const simplecpp::Token *cmdtok = tok->next;
493  if (!sameline(tok, cmdtok))
494  continue;
495  if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef" || cmdtok->str() == "if") {
496  std::string config;
497  if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef") {
498  const simplecpp::Token *expr1 = cmdtok->next;
499  if (sameline(tok,expr1) && expr1->name && !sameline(tok,expr1->next))
500  config = expr1->str();
501  if (defined.find(config) != defined.end())
502  config.clear();
503  } else if (cmdtok->str() == "if") {
504  config = readcondition(cmdtok, defined, undefined);
505  }
506 
507  // skip undefined configurations..
508  if (isUndefined(config, undefined))
509  config.clear();
510 
511  bool ifndef = false;
512  if (cmdtok->str() == "ifndef")
513  ifndef = true;
514  else {
515  const std::array<std::string, 6> match{"if", "!", "defined", "(", config, ")"};
516  int i = 0;
517  ifndef = true;
518  for (const simplecpp::Token *t = cmdtok; i < match.size(); t = t->next) {
519  if (!t || t->str() != match[i++]) {
520  ifndef = false;
521  break;
522  }
523  }
524  }
525 
526  // include guard..
527  if (ifndef && tok->location.fileIndex > 0) {
528  bool includeGuard = true;
529  for (const simplecpp::Token *t = tok->previous; t; t = t->previous) {
530  if (t->location.fileIndex == tok->location.fileIndex) {
531  includeGuard = false;
532  break;
533  }
534  }
535  if (includeGuard) {
536  configs_if.emplace_back(/*std::string()*/);
537  configs_ifndef.emplace_back(/*std::string()*/);
538  continue;
539  }
540  }
541 
542  configs_if.push_back((cmdtok->str() == "ifndef") ? std::string() : config);
543  configs_ifndef.push_back((cmdtok->str() == "ifndef") ? std::move(config) : std::string());
544  ret.insert(cfg(configs_if,userDefines));
545  } else if (cmdtok->str() == "elif" || cmdtok->str() == "else") {
546  if (getConfigsElseIsFalse(configs_if,userDefines)) {
547  tok = gotoEndIf(tok);
548  if (!tok)
549  break;
550  tok = tok->previous;
551  continue;
552  }
553  if (cmdtok->str() == "else" &&
554  cmdtok->next &&
555  !sameline(cmdtok,cmdtok->next) &&
556  sameline(cmdtok->next, cmdtok->next->next) &&
557  cmdtok->next->op == '#' &&
558  cmdtok->next->next->str() == "error") {
559  const std::string &ifcfg = cfg(configs_if, userDefines);
560  if (!ifcfg.empty()) {
561  if (!elseError.empty())
562  elseError += ';';
563  elseError += ifcfg;
564  }
565  }
566  if (!configs_if.empty())
567  configs_if.pop_back();
568  if (cmdtok->str() == "elif") {
569  std::string config = readcondition(cmdtok, defined, undefined);
570  if (isUndefined(config,undefined))
571  config.clear();
572  configs_if.push_back(std::move(config));
573  ret.insert(cfg(configs_if, userDefines));
574  } else if (!configs_ifndef.empty()) {
575  configs_if.push_back(configs_ifndef.back());
576  ret.insert(cfg(configs_if, userDefines));
577  }
578  } else if (cmdtok->str() == "endif" && !sameline(tok, cmdtok->next)) {
579  if (!configs_if.empty())
580  configs_if.pop_back();
581  if (!configs_ifndef.empty())
582  configs_ifndef.pop_back();
583  } else if (cmdtok->str() == "error") {
584  if (!configs_ifndef.empty() && !configs_ifndef.back().empty()) {
585  if (configs_ifndef.size() == 1U)
586  ret.erase(emptyString);
587  std::vector<std::string> configs(configs_if);
588  configs.push_back(configs_ifndef.back());
589  ret.erase(cfg(configs, userDefines));
590  std::set<std::string> temp;
591  temp.swap(ret);
592  for (const std::string &c: temp) {
593  if (c.find(configs_ifndef.back()) != std::string::npos)
594  ret.insert(c);
595  else if (c.empty())
596  ret.insert("");
597  else
598  ret.insert(c + ";" + configs_ifndef.back());
599  }
600  if (!elseError.empty())
601  elseError += ';';
602  elseError += cfg(configs_ifndef, userDefines);
603  }
604  if (!configs_if.empty() && !configs_if.back().empty()) {
605  const std::string &last = configs_if.back();
606  if (last.size() > 2U && last.compare(last.size()-2U,2,"=0") == 0) {
607  std::vector<std::string> configs(configs_if);
608  ret.erase(cfg(configs, userDefines));
609  configs[configs.size() - 1U] = last.substr(0,last.size()-2U);
610  if (configs.size() == 1U)
611  ret.erase("");
612  if (!elseError.empty())
613  elseError += ';';
614  elseError += cfg(configs, userDefines);
615  }
616  }
617  } else if (cmdtok->str() == "define" && sameline(tok, cmdtok->next) && cmdtok->next->name) {
618  defined.insert(cmdtok->next->str());
619  }
620  }
621  if (!elseError.empty())
622  ret.insert(std::move(elseError));
623 }
624 
625 
626 std::set<std::string> Preprocessor::getConfigs(const simplecpp::TokenList &tokens) const
627 {
628  std::set<std::string> ret = { "" };
629  if (!tokens.cfront())
630  return ret;
631 
632  std::set<std::string> defined = { "__cplusplus" };
633 
634  ::getConfigs(tokens, defined, mSettings.userDefines, mSettings.userUndefs, ret);
635 
636  for (std::map<std::string, simplecpp::TokenList*>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) {
637  if (!mSettings.configurationExcluded(it->first))
638  ::getConfigs(*(it->second), defined, mSettings.userDefines, mSettings.userUndefs, ret);
639  }
640 
641  return ret;
642 }
643 
644 static void splitcfg(const std::string &cfg, std::list<std::string> &defines, const std::string &defaultValue)
645 {
646  for (std::string::size_type defineStartPos = 0U; defineStartPos < cfg.size();) {
647  const std::string::size_type defineEndPos = cfg.find(';', defineStartPos);
648  std::string def = (defineEndPos == std::string::npos) ? cfg.substr(defineStartPos) : cfg.substr(defineStartPos, defineEndPos - defineStartPos);
649  if (!defaultValue.empty() && def.find('=') == std::string::npos)
650  def += '=' + defaultValue;
651  defines.push_back(std::move(def));
652  if (defineEndPos == std::string::npos)
653  break;
654  defineStartPos = defineEndPos + 1U;
655  }
656 }
657 
658 static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename)
659 {
660  // TODO: make it possible to specify platform-dependent sizes
661  simplecpp::DUI dui;
662 
663  splitcfg(mSettings.userDefines, dui.defines, "1");
664  if (!cfg.empty())
665  splitcfg(cfg, dui.defines, emptyString);
666 
667  for (const std::string &def : mSettings.library.defines) {
668  const std::string::size_type pos = def.find_first_of(" (");
669  if (pos == std::string::npos) {
670  dui.defines.push_back(def);
671  continue;
672  }
673  std::string s = def;
674  if (s[pos] == ' ') {
675  s[pos] = '=';
676  } else {
677  s[s.find(')')+1] = '=';
678  }
679  dui.defines.push_back(std::move(s));
680  }
681 
682  dui.undefined = mSettings.userUndefs; // -U
683  dui.includePaths = mSettings.includePaths; // -I
684  dui.includes = mSettings.userIncludes; // --include
685  // TODO: use mSettings.standards.stdValue instead
686  // TODO: error out on unknown language?
687  const Standards::Language lang = Path::identify(filename);
688  if (lang == Standards::Language::CPP) {
689  dui.std = mSettings.standards.getCPP();
690  splitcfg(mSettings.platform.getLimitsDefines(Standards::getCPP(dui.std)), dui.defines, "");
691  }
692  else if (lang == Standards::Language::C) {
693  dui.std = mSettings.standards.getC();
694  splitcfg(mSettings.platform.getLimitsDefines(Standards::getC(dui.std)), dui.defines, "");
695  }
696  dui.clearIncludeCache = mSettings.clearIncludeCache;
697  return dui;
698 }
699 
700 bool Preprocessor::hasErrors(const simplecpp::Output &output)
701 {
702  switch (output.type) {
703  case simplecpp::Output::ERROR:
704  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
705  case simplecpp::Output::SYNTAX_ERROR:
706  case simplecpp::Output::UNHANDLED_CHAR_ERROR:
707  case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND:
708  case simplecpp::Output::FILE_NOT_FOUND:
709  return true;
710  case simplecpp::Output::WARNING:
711  case simplecpp::Output::MISSING_HEADER:
712  case simplecpp::Output::PORTABILITY_BACKSLASH:
713  break;
714  }
715  return false;
716 }
717 
718 bool Preprocessor::hasErrors(const simplecpp::OutputList &outputList)
719 {
720  const auto it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output) {
721  return hasErrors(output);
722  });
723  return it != outputList.cend();
724 }
725 
726 void Preprocessor::handleErrors(const simplecpp::OutputList& outputList, bool throwError)
727 {
728  const bool showerror = (!mSettings.userDefines.empty() && !mSettings.force);
729  reportOutput(outputList, showerror);
730  if (throwError) {
731  const auto it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output){
732  return hasErrors(output);
733  });
734  if (it != outputList.cend()) {
735  throw *it;
736  }
737  }
738 }
739 
740 bool Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector<std::string> &files)
741 {
742  const simplecpp::DUI dui = createDUI(mSettings, emptyString, files[0]);
743 
744  simplecpp::OutputList outputList;
745  mTokenLists = simplecpp::load(rawtokens, files, dui, &outputList);
746  handleErrors(outputList, false);
747  return !hasErrors(outputList);
748 }
749 
751 {
752  for (std::pair<const std::string, simplecpp::TokenList*>& tokenList : mTokenLists) {
753  if (tokenList.second)
754  tokenList.second->removeComments();
755  }
756 }
757 
758 void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const
759 {
760  tokens->sizeOfType["bool"] = mSettings.platform.sizeof_bool;
761  tokens->sizeOfType["short"] = mSettings.platform.sizeof_short;
762  tokens->sizeOfType["int"] = mSettings.platform.sizeof_int;
763  tokens->sizeOfType["long"] = mSettings.platform.sizeof_long;
764  tokens->sizeOfType["long long"] = mSettings.platform.sizeof_long_long;
765  tokens->sizeOfType["float"] = mSettings.platform.sizeof_float;
766  tokens->sizeOfType["double"] = mSettings.platform.sizeof_double;
767  tokens->sizeOfType["long double"] = mSettings.platform.sizeof_long_double;
768  tokens->sizeOfType["bool *"] = mSettings.platform.sizeof_pointer;
769  tokens->sizeOfType["short *"] = mSettings.platform.sizeof_pointer;
770  tokens->sizeOfType["int *"] = mSettings.platform.sizeof_pointer;
771  tokens->sizeOfType["long *"] = mSettings.platform.sizeof_pointer;
772  tokens->sizeOfType["long long *"] = mSettings.platform.sizeof_pointer;
773  tokens->sizeOfType["float *"] = mSettings.platform.sizeof_pointer;
774  tokens->sizeOfType["double *"] = mSettings.platform.sizeof_pointer;
775  tokens->sizeOfType["long double *"] = mSettings.platform.sizeof_pointer;
776 }
777 
778 simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, bool throwError)
779 {
780  const simplecpp::DUI dui = createDUI(mSettings, cfg, files[0]);
781 
782  simplecpp::OutputList outputList;
783  std::list<simplecpp::MacroUsage> macroUsage;
784  std::list<simplecpp::IfCond> ifCond;
785  simplecpp::TokenList tokens2(files);
786  simplecpp::preprocess(tokens2, tokens1, files, mTokenLists, dui, &outputList, &macroUsage, &ifCond);
787  mMacroUsage = std::move(macroUsage);
788  mIfCond = std::move(ifCond);
789 
790  handleErrors(outputList, throwError);
791 
792  tokens2.removeComments();
793 
794  return tokens2;
795 }
796 
797 std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, const bool writeLocations)
798 {
799  simplecpp::TokenList tokens2 = preprocess(tokens1, cfg, files, false);
800  unsigned int prevfile = 0;
801  unsigned int line = 1;
802  std::ostringstream ret;
803  for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) {
804  if (writeLocations && tok->location.fileIndex != prevfile) {
805  ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
806  prevfile = tok->location.fileIndex;
807  line = tok->location.line;
808  }
809 
810  if (tok->previous && line >= tok->location.line) // #7912
811  ret << ' ';
812  while (tok->location.line > line) {
813  ret << '\n';
814  line++;
815  }
816  if (!tok->macro.empty())
818  ret << tok->str();
819  }
820 
821  return ret.str();
822 }
823 
824 void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror)
825 {
826  for (const simplecpp::Output &out : outputList) {
827  switch (out.type) {
828  case simplecpp::Output::ERROR:
829  if (!startsWith(out.msg,"#error") || showerror)
830  error(out.location.file(), out.location.line, out.msg);
831  break;
832  case simplecpp::Output::WARNING:
833  case simplecpp::Output::PORTABILITY_BACKSLASH:
834  break;
835  case simplecpp::Output::MISSING_HEADER: {
836  const std::string::size_type pos1 = out.msg.find_first_of("<\"");
837  const std::string::size_type pos2 = out.msg.find_first_of(">\"", pos1 + 1U);
838  if (pos1 < pos2 && pos2 != std::string::npos)
839  missingInclude(out.location.file(), out.location.line, out.msg.substr(pos1+1, pos2-pos1-1), out.msg[pos1] == '\"' ? UserHeader : SystemHeader);
840  }
841  break;
842  case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
843  case simplecpp::Output::SYNTAX_ERROR:
844  case simplecpp::Output::UNHANDLED_CHAR_ERROR:
845  error(out.location.file(), out.location.line, out.msg);
846  break;
847  case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND:
848  case simplecpp::Output::FILE_NOT_FOUND:
849  error(emptyString, 0, out.msg);
850  break;
851  }
852  }
853 }
854 
855 void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg)
856 {
857  std::list<ErrorMessage::FileLocation> locationList;
858  if (!filename.empty()) {
859  std::string file = Path::fromNativeSeparators(filename);
862 
863  locationList.emplace_back(file, linenr, 0);
864  }
865  mErrorLogger.reportErr(ErrorMessage(std::move(locationList),
866  mFile0,
868  msg,
869  "preprocessorErrorDirective",
871 }
872 
873 // Report that include is missing
874 void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
875 {
877  return;
878 
879  std::list<ErrorMessage::FileLocation> locationList;
880  if (!filename.empty()) {
881  locationList.emplace_back(filename, linenr, 0);
882  }
883  ErrorMessage errmsg(std::move(locationList), mFile0, Severity::information,
884  (headerType==SystemHeader) ?
885  "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." :
886  "Include file: \"" + header + "\" not found.",
887  (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude",
889  mErrorLogger.reportErr(errmsg);
890 }
891 
892 void Preprocessor::getErrorMessages(ErrorLogger &errorLogger, const Settings &settings)
893 {
894  Preprocessor preprocessor(settings, errorLogger);
897  preprocessor.error(emptyString, 1, "#error message"); // #error ..
898 }
899 
900 void Preprocessor::dump(std::ostream &out) const
901 {
902  // Create a xml dump.
903 
904  if (!mMacroUsage.empty()) {
905  out << " <macro-usage>" << std::endl;
906  for (const simplecpp::MacroUsage &macroUsage: mMacroUsage) {
907  out << " <macro"
908  << " name=\"" << macroUsage.macroName << "\""
909  << " file=\"" << ErrorLogger::toxml(macroUsage.macroLocation.file()) << "\""
910  << " line=\"" << macroUsage.macroLocation.line << "\""
911  << " column=\"" << macroUsage.macroLocation.col << "\""
912  << " usefile=\"" << ErrorLogger::toxml(macroUsage.useLocation.file()) << "\""
913  << " useline=\"" << macroUsage.useLocation.line << "\""
914  << " usecolumn=\"" << macroUsage.useLocation.col << "\""
915  << " is-known-value=\"" << bool_to_string(macroUsage.macroValueKnown) << "\""
916  << "/>" << std::endl;
917  }
918  out << " </macro-usage>" << std::endl;
919  }
920 
921  if (!mIfCond.empty()) {
922  out << " <simplecpp-if-cond>" << std::endl;
923  for (const simplecpp::IfCond &ifCond: mIfCond) {
924  out << " <if-cond"
925  << " file=\"" << ErrorLogger::toxml(ifCond.location.file()) << "\""
926  << " line=\"" << ifCond.location.line << "\""
927  << " column=\"" << ifCond.location.col << "\""
928  << " E=\"" << ErrorLogger::toxml(ifCond.E) << "\""
929  << " result=\"" << ifCond.result << "\""
930  << "/>" << std::endl;
931  }
932  out << " </simplecpp-if-cond>" << std::endl;
933  }
934 }
935 
936 std::size_t Preprocessor::calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
937 {
938  std::string hashData = toolinfo;
939  for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
940  if (!tok->comment)
941  hashData += tok->str();
942  }
943  for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) {
944  for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) {
945  if (!tok->comment)
946  hashData += tok->str();
947  }
948  }
949  return (std::hash<std::string>{})(hashData);
950 }
951 
952 void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList *tokenList) const
953 {
955  for (const std::pair<const std::string, simplecpp::TokenList*>& list : mTokenLists) {
957  }
958 }
959 
960 void Preprocessor::simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList)
961 {
962  // assembler code..
963  for (simplecpp::Token *tok = tokenList->front(); tok; tok = tok->next) {
964  if (tok->op != '#')
965  continue;
966  if (sameline(tok, tok->previousSkipComments()))
967  continue;
968 
969  const simplecpp::Token * const tok2 = tok->nextSkipComments();
970  if (!tok2 || !sameline(tok, tok2) || tok2->str() != "pragma")
971  continue;
972 
973  const simplecpp::Token * const tok3 = tok2->nextSkipComments();
974  if (!tok3 || !sameline(tok, tok3) || tok3->str() != "asm")
975  continue;
976 
977  const simplecpp::Token *endasm = tok3;
978  while ((endasm = endasm->next) != nullptr) {
979  if (endasm->op != '#' || sameline(endasm,endasm->previousSkipComments()))
980  continue;
981  const simplecpp::Token * const endasm2 = endasm->nextSkipComments();
982  if (!endasm2 || !sameline(endasm, endasm2) || endasm2->str() != "pragma")
983  continue;
984  const simplecpp::Token * const endasm3 = endasm2->nextSkipComments();
985  if (!endasm3 || !sameline(endasm2, endasm3) || endasm3->str() != "endasm")
986  continue;
987  while (sameline(endasm,endasm3))
988  endasm = endasm->next;
989  break;
990  }
991 
992  const simplecpp::Token * const tok4 = tok3->next;
993  tok->setstr("asm");
994  const_cast<simplecpp::Token *>(tok2)->setstr("(");
995  const_cast<simplecpp::Token *>(tok3)->setstr(")");
996  const_cast<simplecpp::Token *>(tok4)->setstr(";");
997  while (tok4->next != endasm)
998  tokenList->deleteToken(tok4->next);
999  }
1000 }
static bool match(const Token *tok, const std::string &rhs)
Definition: astutils.cpp:342
This is an interface, which the class responsible of error logging should implement.
Definition: errorlogger.h:214
virtual void reportErr(const 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:48
std::set< std::string > defines
Definition: library.h:431
static std::string simplifyPath(std::string originalPath)
Simplify path "foo/bar/.." => "foo".
Definition: path.cpp:83
static std::string fromNativeSeparators(std::string path)
Convert path to use internal path separators.
Definition: path.cpp:75
static Standards::Language identify(const std::string &path, bool *header=nullptr)
Identify the language based on the file extension.
Definition: path.cpp:248
static std::string getRelativePath(const std::string &absolutePath, const std::vector< std::string > &basePaths)
Create a relative path from an absolute one, if absolute path is inside the basePaths.
Definition: path.cpp:181
std::size_t sizeof_bool
bits in long long
Definition: platform.h:92
std::size_t sizeof_long_long
Definition: platform.h:96
std::size_t sizeof_long_double
Definition: platform.h:99
std::size_t sizeof_short
Definition: platform.h:93
std::size_t sizeof_int
Definition: platform.h:94
std::size_t sizeof_pointer
Definition: platform.h:102
std::size_t sizeof_float
Definition: platform.h:97
std::string getLimitsDefines(bool c99) const
provides list of defines specified by the limit.h/climits includes
Definition: platform.cpp:298
std::size_t sizeof_double
Definition: platform.h:98
std::size_t sizeof_long
Definition: platform.h:95
The cppcheck preprocessor.
Definition: preprocessor.h:71
void error(const std::string &filename, unsigned int linenr, const std::string &msg)
void handleErrors(const simplecpp::OutputList &outputList, bool throwError)
void reportOutput(const simplecpp::OutputList &outputList, bool showerror)
void setPlatformInfo(simplecpp::TokenList *tokens) const
void simplifyPragmaAsm(simplecpp::TokenList *tokenList) const
virtual ~Preprocessor()
bool loadFiles(const simplecpp::TokenList &rawtokens, std::vector< std::string > &files)
void missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
HeaderTypes
Include file types.
Definition: preprocessor.h:82
Preprocessor(const Settings &settings, ErrorLogger &errorLogger)
static void simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList)
std::set< std::string > getConfigs(const simplecpp::TokenList &tokens) const
void removeComments()
static void getErrorMessages(ErrorLogger &errorLogger, const Settings &settings)
std::size_t calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
Calculate HASH.
std::string getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector< std::string > &files, const bool writeLocations)
std::list< simplecpp::MacroUsage > mMacroUsage
simplecpp tracking info
Definition: preprocessor.h:152
std::list< simplecpp::IfCond > mIfCond
Definition: preprocessor.h:153
std::string mFile0
filename for cpp/c file - useful when reporting errors
Definition: preprocessor.h:149
const Settings & mSettings
Definition: preprocessor.h:141
std::map< std::string, simplecpp::TokenList * > mTokenLists
list of all directives met while preprocessing file
Definition: preprocessor.h:146
static bool hasErrors(const simplecpp::Output &output)
simplecpp::TokenList preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector< std::string > &files, bool throwError=false)
static char macroChar
character that is inserted in expanded macros
Definition: preprocessor.h:88
ErrorLogger & mErrorLogger
Definition: preprocessor.h:142
void dump(std::ostream &out) const
dump all directives present in source file
void inlineSuppressions(const simplecpp::TokenList &tokens, SuppressionList &suppressions)
std::list< Directive > createDirectives(const simplecpp::TokenList &tokens) const
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
std::set< std::string > userUndefs
undefines given by the user
Definition: settings.h:389
bool clearIncludeCache
Internal: Clear the simplecpp non-existing include cache.
Definition: settings.h:159
std::vector< std::string > basePaths
Paths used as base for conversion to relative paths.
Definition: settings.h:118
bool configurationExcluded(const std::string &file) const
return true if a included file is to be excluded in Preprocessor::getConfigs
Definition: settings.h:410
SimpleEnableGroup< Checks > checks
Definition: settings.h:360
bool relativePaths
Use relative paths in output.
Definition: settings.h:285
Library library
Library.
Definition: settings.h:237
std::string userDefines
defines given by the user
Definition: settings.h:386
bool inlineSuppressions
Is –inline-suppr given?
Definition: settings.h:227
Platform platform
Definition: settings.h:255
bool force
Force checking the files with "too many" configurations (–force).
Definition: settings.h:220
std::list< std::string > includePaths
List of include paths, e.g.
Definition: settings.h:224
std::list< std::string > userIncludes
forced includes given by the user
Definition: settings.h:392
Standards standards
Struct contains standards settings.
Definition: settings.h:366
bool isEnabled(T flag) const
Definition: settings.h:66
class for handling suppressions
Definition: suppressions.h:42
static std::vector< Suppression > parseMultiSuppressComment(const std::string &comment, std::string *errorMessage)
Parse multi inline suppression in comment.
std::string addSuppression(Suppression suppression)
Don't show this error.
static const std::string emptyString
Definition: config.h:127
@ information
Checking information.
@ error
Programming error.
@ missingInclude
static bool hasDefine(const std::string &userDefines, const std::string &cfg)
static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Settings &settings, SuppressionList &suppressions, std::list< BadInlineSuppression > &bad)
static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std::list< SuppressionList::Suppression > &inlineSuppressions, std::list< BadInlineSuppression > &bad)
static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename)
static bool isUndefined(const std::string &cfg, const std::set< std::string > &undefined)
static std::string readcondition(const simplecpp::Token *iftok, const std::set< std::string > &defined, const std::set< std::string > &undefined)
static const simplecpp::Token * gotoEndIf(const simplecpp::Token *cmdtok)
static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
static void splitcfg(const std::string &cfg, std::list< std::string > &defines, const std::string &defaultValue)
static std::string cfg(const std::vector< std::string > &configs, const std::string &userDefines)
static bool getConfigsElseIsFalse(const std::vector< std::string > &configs_if, const std::string &userDefines)
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)
A preprocessor directive Each preprocessor directive (#include, #define, #undef, #if,...
Definition: preprocessor.h:49
Directive(std::string _file, const int _linenr, const std::string &_str)
record a directive (possibly filtering src)
std::string str
the actual directive text
Definition: preprocessor.h:57
unsigned int linenr
line number in (possibly included) file where directive is defined
Definition: preprocessor.h:54
std::string getC() const
Definition: standards.h:64
std::string getCPP() const
Definition: standards.h:103
bool parseComment(std::string comment, std::string *errorMessage)
Parse inline suppression in comment.
std::string trim(const std::string &s, const std::string &t)
Remove heading and trailing whitespaces from the input parameter.
Definition: utils.cpp:133
bool startsWith(const std::string &str, const char start[], std::size_t startlen)
Definition: utils.h:94
static const char * bool_to_string(bool b)
Definition: utils.h:345