Cppcheck
importproject.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 #include "importproject.h"
20 
21 #include "path.h"
22 #include "settings.h"
23 #include "standards.h"
24 #include "suppressions.h"
25 #include "token.h"
26 #include "tokenlist.h"
27 #include "utils.h"
28 
29 #include <algorithm>
30 #include <cstdlib>
31 #include <cstring>
32 #include <fstream>
33 #include <iostream>
34 #include <iterator>
35 #include <sstream>
36 #include <unordered_set>
37 #include <utility>
38 
39 #include "xml.h"
40 
41 #include "json.h"
42 
43 // TODO: align the exclusion logic with PathMatch
44 void ImportProject::ignorePaths(const std::vector<std::string> &ipaths)
45 {
46  for (std::list<FileSettings>::iterator it = fileSettings.begin(); it != fileSettings.end();) {
47  bool ignore = false;
48  for (std::string i : ipaths) {
49  if (it->filename().size() > i.size() && it->filename().compare(0,i.size(),i)==0) {
50  ignore = true;
51  break;
52  }
53  if (isValidGlobPattern(i) && matchglob(i, it->filename())) {
54  ignore = true;
55  break;
56  }
57  if (!Path::isAbsolute(i)) {
58  i = mPath + i;
59  if (it->filename().size() > i.size() && it->filename().compare(0,i.size(),i)==0) {
60  ignore = true;
61  break;
62  }
63  }
64  }
65  if (ignore)
66  it = fileSettings.erase(it);
67  else
68  ++it;
69  }
70 }
71 
72 void ImportProject::ignoreOtherConfigs(const std::string &cfg)
73 {
74  for (std::list<FileSettings>::iterator it = fileSettings.begin(); it != fileSettings.end();) {
75  if (it->cfg != cfg)
76  it = fileSettings.erase(it);
77  else
78  ++it;
79  }
80 }
81 
82 void ImportProject::fsSetDefines(FileSettings& fs, std::string defs)
83 {
84  while (defs.find(";%(") != std::string::npos) {
85  const std::string::size_type pos1 = defs.find(";%(");
86  const std::string::size_type pos2 = defs.find(';', pos1+1);
87  defs.erase(pos1, pos2 == std::string::npos ? pos2 : (pos2-pos1));
88  }
89  while (defs.find(";;") != std::string::npos)
90  defs.erase(defs.find(";;"),1);
91  while (!defs.empty() && defs[0] == ';')
92  defs.erase(0, 1);
93  while (!defs.empty() && endsWith(defs,';'))
94  defs.erase(defs.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it
95  bool eq = false;
96  for (std::size_t pos = 0; pos < defs.size(); ++pos) {
97  if (defs[pos] == '(' || defs[pos] == '=')
98  eq = true;
99  else if (defs[pos] == ';') {
100  if (!eq) {
101  defs.insert(pos,"=1");
102  pos += 3;
103  }
104  if (pos < defs.size())
105  eq = false;
106  }
107  }
108  if (!eq && !defs.empty())
109  defs += "=1";
110  fs.defines.swap(defs);
111 }
112 
113 static bool simplifyPathWithVariables(std::string &s, std::map<std::string, std::string, cppcheck::stricmp> &variables)
114 {
115  std::set<std::string, cppcheck::stricmp> expanded;
116  std::string::size_type start = 0;
117  while ((start = s.find("$(")) != std::string::npos) {
118  const std::string::size_type end = s.find(')',start);
119  if (end == std::string::npos)
120  break;
121  const std::string var = s.substr(start+2,end-start-2);
122  if (expanded.find(var) != expanded.end())
123  break;
124  expanded.insert(var);
125  std::map<std::string, std::string, cppcheck::stricmp>::const_iterator it1 = variables.find(var);
126  // variable was not found within defined variables
127  if (it1 == variables.end()) {
128  const char *envValue = std::getenv(var.c_str());
129  if (!envValue) {
130  //! \todo generate a debug/info message about undefined variable
131  break;
132  }
133  variables[var] = std::string(envValue);
134  it1 = variables.find(var);
135  }
136  s.replace(start, end - start + 1, it1->second);
137  }
138  if (s.find("$(") != std::string::npos)
139  return false;
141  return true;
142 }
143 
144 void ImportProject::fsSetIncludePaths(FileSettings& fs, const std::string &basepath, const std::list<std::string> &in, std::map<std::string, std::string, cppcheck::stricmp> &variables)
145 {
146  std::set<std::string> found;
147  // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
148  const std::list<std::string> copyIn(in);
149  fs.includePaths.clear();
150  for (const std::string &ipath : copyIn) {
151  if (ipath.empty())
152  continue;
153  if (startsWith(ipath,"%("))
154  continue;
155  std::string s(Path::fromNativeSeparators(ipath));
156  if (!found.insert(s).second)
157  continue;
158  if (s[0] == '/' || (s.size() > 1U && s.compare(1,2,":/") == 0)) {
159  if (!endsWith(s,'/'))
160  s += '/';
161  fs.includePaths.push_back(std::move(s));
162  continue;
163  }
164 
165  if (endsWith(s,'/')) // this is a temporary hack, simplifyPath can crash if path ends with '/'
166  s.erase(s.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it
167 
168  if (s.find("$(") == std::string::npos) {
169  s = Path::simplifyPath(basepath + s);
170  } else {
171  if (!simplifyPathWithVariables(s, variables))
172  continue;
173  }
174  if (s.empty())
175  continue;
176  fs.includePaths.push_back(s + '/');
177  }
178 }
179 
180 ImportProject::Type ImportProject::import(const std::string &filename, Settings *settings)
181 {
182  std::ifstream fin(filename);
183  if (!fin.is_open())
185 
187  if (!mPath.empty() && !endsWith(mPath,'/'))
188  mPath += '/';
189 
190  const std::vector<std::string> fileFilters =
191  settings ? settings->fileFilters : std::vector<std::string>();
192 
193  if (endsWith(filename, ".json")) {
194  if (importCompileCommands(fin)) {
195  setRelativePaths(filename);
197  }
198  } else if (endsWith(filename, ".sln")) {
199  if (importSln(fin, mPath, fileFilters)) {
200  setRelativePaths(filename);
202  }
203  } else if (endsWith(filename, ".vcxproj")) {
204  std::map<std::string, std::string, cppcheck::stricmp> variables;
205  if (importVcxproj(filename, variables, emptyString, fileFilters)) {
206  setRelativePaths(filename);
208  }
209  } else if (endsWith(filename, ".bpr")) {
210  if (importBcb6Prj(filename)) {
211  setRelativePaths(filename);
213  }
214  } else if (settings && endsWith(filename, ".cppcheck")) {
215  if (importCppcheckGuiProject(fin, settings)) {
216  setRelativePaths(filename);
218  }
219  } else {
221  }
223 }
224 
225 static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[])
226 {
227  std::string ret;
228  bool escapedString = false;
229  bool str = false;
230  bool escape = false;
231  for (; *pos < command.size() && (str || !std::strchr(until, command[*pos])); (*pos)++) {
232  if (escape)
233  escape = false;
234  else if (command[*pos] == '\\') {
235  if (str)
236  escape = true;
237  else if (command[*pos + 1] == '"') {
238  if (escapedString)
239  return ret + "\\\"";
240  escapedString = true;
241  ret += "\\\"";
242  (*pos)++;
243  continue;
244  }
245  } else if (command[*pos] == '\"')
246  str = !str;
247  ret += command[*pos];
248  }
249  return ret;
250 }
251 
252 static std::string unescape(const std::string &in)
253 {
254  std::string out;
255  bool escape = false;
256  for (const char c: in) {
257  if (escape) {
258  escape = false;
259  if (!std::strchr("\\\"\'",c))
260  out += "\\";
261  out += c;
262  } else if (c == '\\')
263  escape = true;
264  else
265  out += c;
266  }
267  return out;
268 }
269 
270 void ImportProject::fsParseCommand(FileSettings& fs, const std::string& command)
271 {
272  std::string defs;
273 
274  // Parse command..
275  std::string::size_type pos = 0;
276  while (std::string::npos != (pos = command.find(' ',pos))) {
277  while (pos < command.size() && command[pos] == ' ')
278  pos++;
279  if (pos >= command.size())
280  break;
281  if (command[pos] != '/' && command[pos] != '-')
282  continue;
283  pos++;
284  if (pos >= command.size())
285  break;
286  const char F = command[pos++];
287  if (std::strchr("DUI", F)) {
288  while (pos < command.size() && command[pos] == ' ')
289  ++pos;
290  }
291  const std::string fval = readUntil(command, &pos, " =");
292  if (F=='D') {
293  std::string defval = readUntil(command, &pos, " ");
294  defs += fval;
295  if (defval.size() >= 3 && startsWith(defval,"=\"") && defval.back()=='\"')
296  defval = "=" + unescape(defval.substr(2, defval.size() - 3));
297  else if (defval.size() >= 5 && startsWith(defval, "=\\\"") && endsWith(defval, "\\\""))
298  defval = "=\"" + unescape(defval.substr(3, defval.size() - 5)) + "\"";
299  if (!defval.empty())
300  defs += defval;
301  defs += ';';
302  } else if (F=='U')
303  fs.undefs.insert(fval);
304  else if (F=='I') {
305  std::string i = fval;
306  if (i.size() > 1 && i[0] == '\"' && i.back() == '\"')
307  i = unescape(i.substr(1, i.size() - 2));
308  if (std::find(fs.includePaths.cbegin(), fs.includePaths.cend(), i) == fs.includePaths.cend())
309  fs.includePaths.push_back(std::move(i));
310  } else if (F=='s' && startsWith(fval,"td")) {
311  ++pos;
312  fs.standard = readUntil(command, &pos, " ");
313  } else if (F == 'i' && fval == "system") {
314  ++pos;
315  std::string isystem = readUntil(command, &pos, " ");
316  fs.systemIncludePaths.push_back(std::move(isystem));
317  } else if (F=='m') {
318  if (fval == "unicode") {
319  defs += "UNICODE";
320  defs += ";";
321  }
322  } else if (F=='f') {
323  if (fval == "pic") {
324  defs += "__pic__";
325  defs += ";";
326  } else if (fval == "PIC") {
327  defs += "__PIC__";
328  defs += ";";
329  } else if (fval == "pie") {
330  defs += "__pie__";
331  defs += ";";
332  } else if (fval == "PIE") {
333  defs += "__PIE__";
334  defs += ";";
335  }
336  // TODO: support -fsigned-char and -funsigned-char?
337  // we can only set it globally but in this context it needs to be treated per file
338  }
339  }
340  fsSetDefines(fs, std::move(defs));
341 }
342 
343 bool ImportProject::importCompileCommands(std::istream &istr)
344 {
345  picojson::value compileCommands;
346  istr >> compileCommands;
347  if (!compileCommands.is<picojson::array>()) {
348  printError("compilation database is not a JSON array");
349  return false;
350  }
351 
352  for (const picojson::value &fileInfo : compileCommands.get<picojson::array>()) {
353  picojson::object obj = fileInfo.get<picojson::object>();
354  std::string dirpath = Path::fromNativeSeparators(obj["directory"].get<std::string>());
355 
356  /* CMAKE produces the directory without trailing / so add it if not
357  * there - it is needed by setIncludePaths() */
358  if (!endsWith(dirpath, '/'))
359  dirpath += '/';
360 
361  const std::string directory = std::move(dirpath);
362 
363  std::string command;
364  if (obj.count("arguments")) {
365  if (obj["arguments"].is<picojson::array>()) {
366  for (const picojson::value& arg : obj["arguments"].get<picojson::array>()) {
367  if (arg.is<std::string>()) {
368  std::string str = arg.get<std::string>();
369  if (str.find(' ') != std::string::npos)
370  str = "\"" + str + "\"";
371  command += str + " ";
372  }
373  }
374  } else {
375  printError("'arguments' field in compilation database entry is not a JSON array");
376  return false;
377  }
378  } else if (obj.count("command")) {
379  if (obj["command"].is<std::string>()) {
380  command = obj["command"].get<std::string>();
381  } else {
382  printError("'command' field in compilation database entry is not a string");
383  return false;
384  }
385  } else {
386  printError("no 'arguments' or 'command' field found in compilation database entry");
387  return false;
388  }
389 
390  if (!obj.count("file") || !obj["file"].is<std::string>()) {
391  printError("skip compilation database entry because it does not have a proper 'file' field");
392  continue;
393  }
394 
395  const std::string file = Path::fromNativeSeparators(obj["file"].get<std::string>());
396 
397  // Accept file?
398  if (!Path::acceptFile(file))
399  continue;
400 
401  std::string path;
402  if (Path::isAbsolute(file))
403  path = Path::simplifyPath(file);
404 #ifdef _WIN32
405  else if (file[0] == '/' && directory.size() > 2 && std::isalpha(directory[0]) && directory[1] == ':')
406  // directory: C:\foo\bar
407  // file: /xy/z.c
408  // => c:/xy/z.c
409  path = Path::simplifyPath(directory.substr(0,2) + file);
410 #endif
411  else
412  path = Path::simplifyPath(directory + file);
413  if (!sourceFileExists(path)) {
414  printError("'" + path + "' from compilation database does not exist");
415  return false;
416  }
417  FileSettings fs{std::move(path)};
418  fsParseCommand(fs, command); // read settings; -D, -I, -U, -std, -m*, -f*
419  std::map<std::string, std::string, cppcheck::stricmp> variables;
420  fsSetIncludePaths(fs, directory, fs.includePaths, variables);
421  fileSettings.push_back(std::move(fs));
422  }
423 
424  return true;
425 }
426 
427 bool ImportProject::importSln(std::istream &istr, const std::string &path, const std::vector<std::string> &fileFilters)
428 {
429  std::string line;
430 
431  if (!std::getline(istr,line)) {
432  printError("Visual Studio solution file is empty");
433  return false;
434  }
435 
436  if (!startsWith(line, "Microsoft Visual Studio Solution File")) {
437  // Skip BOM
438  if (!std::getline(istr, line) || !startsWith(line, "Microsoft Visual Studio Solution File")) {
439  printError("Visual Studio solution file header not found");
440  return false;
441  }
442  }
443 
444  std::map<std::string,std::string,cppcheck::stricmp> variables;
445  variables["SolutionDir"] = path;
446 
447  bool found = false;
448 
449  while (std::getline(istr,line)) {
450  if (!startsWith(line,"Project("))
451  continue;
452  const std::string::size_type pos = line.find(".vcxproj");
453  if (pos == std::string::npos)
454  continue;
455  const std::string::size_type pos1 = line.rfind('\"',pos);
456  if (pos1 == std::string::npos)
457  continue;
458  std::string vcxproj(line.substr(pos1+1, pos-pos1+7));
459  vcxproj = Path::toNativeSeparators(std::move(vcxproj));
460  if (!Path::isAbsolute(vcxproj))
461  vcxproj = path + vcxproj;
462  vcxproj = Path::fromNativeSeparators(std::move(vcxproj));
463  if (!importVcxproj(vcxproj, variables, emptyString, fileFilters)) {
464  printError("failed to load '" + vcxproj + "' from Visual Studio solution");
465  return false;
466  }
467  found = true;
468  }
469 
470  if (!found) {
471  printError("no projects found in Visual Studio solution file");
472  return false;
473  }
474 
475  return true;
476 }
477 
478 namespace {
479  struct ProjectConfiguration {
480  explicit ProjectConfiguration(const tinyxml2::XMLElement *cfg) {
481  const char *a = cfg->Attribute("Include");
482  if (a)
483  name = a;
484  for (const tinyxml2::XMLElement *e = cfg->FirstChildElement(); e; e = e->NextSiblingElement()) {
485  if (!e->GetText())
486  continue;
487  if (std::strcmp(e->Name(),"Configuration")==0)
488  configuration = e->GetText();
489  else if (std::strcmp(e->Name(),"Platform")==0) {
490  platformStr = e->GetText();
491  if (platformStr == "Win32")
492  platform = Win32;
493  else if (platformStr == "x64")
494  platform = x64;
495  else
496  platform = Unknown;
497  }
498  }
499  }
500  std::string name;
501  std::string configuration;
502  enum { Win32, x64, Unknown } platform = Unknown;
503  std::string platformStr;
504  };
505 
506  struct ItemDefinitionGroup {
507  explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, std::string includePaths) : additionalIncludePaths(std::move(includePaths)) {
508  const char *condAttr = idg->Attribute("Condition");
509  if (condAttr)
510  condition = condAttr;
511  for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) {
512  if (std::strcmp(e1->Name(), "ClCompile") == 0) {
513  enhancedInstructionSet = "StreamingSIMDExtensions2";
514  for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
515  if (e->GetText()) {
516  if (std::strcmp(e->Name(), "PreprocessorDefinitions") == 0)
517  preprocessorDefinitions = e->GetText();
518  else if (std::strcmp(e->Name(), "AdditionalIncludeDirectories") == 0) {
519  if (!additionalIncludePaths.empty())
520  additionalIncludePaths += ';';
521  additionalIncludePaths += e->GetText();
522  } else if (std::strcmp(e->Name(), "LanguageStandard") == 0) {
523  if (std::strcmp(e->GetText(), "stdcpp14") == 0)
524  cppstd = Standards::CPP14;
525  else if (std::strcmp(e->GetText(), "stdcpp17") == 0)
526  cppstd = Standards::CPP17;
527  else if (std::strcmp(e->GetText(), "stdcpp20") == 0)
528  cppstd = Standards::CPP20;
529  else if (std::strcmp(e->GetText(), "stdcpplatest") == 0)
530  cppstd = Standards::CPPLatest;
531  } else if (std::strcmp(e->Name(), "EnableEnhancedInstructionSet") == 0) {
532  enhancedInstructionSet = e->GetText();
533  }
534  }
535  }
536  }
537  else if (std::strcmp(e1->Name(), "Link") == 0) {
538  for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
539  if (!e->GetText())
540  continue;
541  if (std::strcmp(e->Name(), "EntryPointSymbol") == 0) {
542  entryPointSymbol = e->GetText();
543  }
544  }
545  }
546  }
547  }
548 
549  static void replaceAll(std::string &c, const std::string &from, const std::string &to) {
550  std::string::size_type pos;
551  while ((pos = c.find(from)) != std::string::npos) {
552  c.erase(pos,from.size());
553  c.insert(pos,to);
554  }
555  }
556 
557  // see https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions
558  // properties are .NET String objects and you can call any of its members on them
559  bool conditionIsTrue(const ProjectConfiguration &p) const {
560  if (condition.empty())
561  return true;
562  std::string c = '(' + condition + ");";
563  replaceAll(c, "$(Configuration)", p.configuration);
564  replaceAll(c, "$(Platform)", p.platformStr);
565 
566  // TODO: improve evaluation
567  const Settings s;
568  TokenList tokenlist(&s);
569  std::istringstream istr(c);
570  tokenlist.createTokens(istr, Standards::Language::C); // TODO: check result
571  for (const Token *tok = tokenlist.front(); tok; tok = tok->next()) {
572  if (tok->str() == "(" && tok->astOperand1() && tok->astOperand2()) {
573  // TODO: this is wrong - it is Contains() not Equals()
574  if (tok->astOperand1()->expressionString() == "Configuration.Contains")
575  return ('\'' + p.configuration + '\'') == tok->astOperand2()->str();
576  }
577  if (tok->str() == "==" && tok->astOperand1() && tok->astOperand2() && tok->astOperand1()->str() == tok->astOperand2()->str())
578  return true;
579  }
580  return false;
581  }
582  std::string condition;
583  std::string enhancedInstructionSet;
584  std::string preprocessorDefinitions;
585  std::string additionalIncludePaths;
586  std::string entryPointSymbol; // TODO: use this
588  };
589 }
590 
591 static std::list<std::string> toStringList(const std::string &s)
592 {
593  std::list<std::string> ret;
594  std::string::size_type pos1 = 0;
595  std::string::size_type pos2;
596  while ((pos2 = s.find(';',pos1)) != std::string::npos) {
597  ret.push_back(s.substr(pos1, pos2-pos1));
598  pos1 = pos2 + 1;
599  if (pos1 >= s.size())
600  break;
601  }
602  if (pos1 < s.size())
603  ret.push_back(s.substr(pos1));
604  return ret;
605 }
606 
607 static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map<std::string,std::string,cppcheck::stricmp> &variables, std::string &includePath, bool *useOfMfc)
608 {
609  if (useOfMfc) {
610  for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) {
611  if (std::strcmp(e->Name(), "UseOfMfc") == 0) {
612  *useOfMfc = true;
613  break;
614  }
615  }
616  }
617 
618  const char* labelAttribute = node->Attribute("Label");
619  if (labelAttribute && std::strcmp(labelAttribute, "UserMacros") == 0) {
620  for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) {
621  const std::string name(propertyGroup->Name());
622  const char *text = propertyGroup->GetText();
623  variables[name] = std::string(text ? text : "");
624  }
625 
626  } else if (!labelAttribute) {
627  for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) {
628  if (std::strcmp(propertyGroup->Name(), "IncludePath") != 0)
629  continue;
630  const char *text = propertyGroup->GetText();
631  if (!text)
632  continue;
633  std::string path(text);
634  const std::string::size_type pos = path.find("$(IncludePath)");
635  if (pos != std::string::npos)
636  path.replace(pos, 14U, includePath);
637  includePath = std::move(path);
638  }
639  }
640 }
641 
642 static void loadVisualStudioProperties(const std::string &props, std::map<std::string,std::string,cppcheck::stricmp> &variables, std::string &includePath, const std::string &additionalIncludeDirectories, std::list<ItemDefinitionGroup> &itemDefinitionGroupList)
643 {
644  std::string filename(props);
645  // variables can't be resolved
646  if (!simplifyPathWithVariables(filename, variables))
647  return;
648 
649  // prepend project dir (if it exists) to transform relative paths into absolute ones
650  if (!Path::isAbsolute(filename) && variables.count("ProjectDir") > 0)
651  filename = Path::getAbsoluteFilePath(variables.at("ProjectDir") + filename);
652 
653  tinyxml2::XMLDocument doc;
654  if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS)
655  return;
656  const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
657  if (rootnode == nullptr)
658  return;
659  for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) {
660  if (std::strcmp(node->Name(), "ImportGroup") == 0) {
661  const char *labelAttribute = node->Attribute("Label");
662  if (labelAttribute == nullptr || std::strcmp(labelAttribute, "PropertySheets") != 0)
663  continue;
664  for (const tinyxml2::XMLElement *importGroup = node->FirstChildElement(); importGroup; importGroup = importGroup->NextSiblingElement()) {
665  if (std::strcmp(importGroup->Name(), "Import") == 0) {
666  const char *projectAttribute = importGroup->Attribute("Project");
667  if (projectAttribute == nullptr)
668  continue;
669  std::string loadprj(projectAttribute);
670  if (loadprj.find('$') == std::string::npos) {
671  loadprj = Path::getPathFromFilename(filename) + loadprj;
672  }
673  loadVisualStudioProperties(loadprj, variables, includePath, additionalIncludeDirectories, itemDefinitionGroupList);
674  }
675  }
676  } else if (std::strcmp(node->Name(),"PropertyGroup")==0) {
677  importPropertyGroup(node, variables, includePath, nullptr);
678  } else if (std::strcmp(node->Name(),"ItemDefinitionGroup")==0) {
679  itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories);
680  }
681  }
682 }
683 
684 bool ImportProject::importVcxproj(const std::string &filename, std::map<std::string, std::string, cppcheck::stricmp> &variables, const std::string &additionalIncludeDirectories, const std::vector<std::string> &fileFilters)
685 {
686  variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename));
687 
688  std::list<ProjectConfiguration> projectConfigurationList;
689  std::list<std::string> compileList;
690  std::list<ItemDefinitionGroup> itemDefinitionGroupList;
691  std::string includePath;
692 
693  bool useOfMfc = false;
694 
695  tinyxml2::XMLDocument doc;
696  const tinyxml2::XMLError error = doc.LoadFile(filename.c_str());
697  if (error != tinyxml2::XML_SUCCESS) {
698  printError(std::string("Visual Studio project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error));
699  return false;
700  }
701  const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
702  if (rootnode == nullptr) {
703  printError("Visual Studio project file has no XML root node");
704  return false;
705  }
706  for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) {
707  if (std::strcmp(node->Name(), "ItemGroup") == 0) {
708  const char *labelAttribute = node->Attribute("Label");
709  if (labelAttribute && std::strcmp(labelAttribute, "ProjectConfigurations") == 0) {
710  for (const tinyxml2::XMLElement *cfg = node->FirstChildElement(); cfg; cfg = cfg->NextSiblingElement()) {
711  if (std::strcmp(cfg->Name(), "ProjectConfiguration") == 0) {
712  const ProjectConfiguration p(cfg);
713  if (p.platform != ProjectConfiguration::Unknown) {
714  projectConfigurationList.emplace_back(cfg);
715  mAllVSConfigs.insert(p.configuration);
716  }
717  }
718  }
719  } else {
720  for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) {
721  if (std::strcmp(e->Name(), "ClCompile") == 0) {
722  const char *include = e->Attribute("Include");
723  if (include && Path::acceptFile(include))
724  compileList.emplace_back(include);
725  }
726  }
727  }
728  } else if (std::strcmp(node->Name(), "ItemDefinitionGroup") == 0) {
729  itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories);
730  } else if (std::strcmp(node->Name(), "PropertyGroup") == 0) {
731  importPropertyGroup(node, variables, includePath, &useOfMfc);
732  } else if (std::strcmp(node->Name(), "ImportGroup") == 0) {
733  const char *labelAttribute = node->Attribute("Label");
734  if (labelAttribute && std::strcmp(labelAttribute, "PropertySheets") == 0) {
735  for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) {
736  if (std::strcmp(e->Name(), "Import") == 0) {
737  const char *projectAttribute = e->Attribute("Project");
738  if (projectAttribute)
739  loadVisualStudioProperties(projectAttribute, variables, includePath, additionalIncludeDirectories, itemDefinitionGroupList);
740  }
741  }
742  }
743  }
744  }
745  // # TODO: support signedness of char via /J (and potential XML option for it)?
746  // we can only set it globally but in this context it needs to be treated per file
747 
748  for (const std::string &c : compileList) {
749  const std::string cfilename = Path::simplifyPath(Path::isAbsolute(c) ? c : Path::getPathFromFilename(filename) + c);
750  if (!fileFilters.empty() && !matchglobs(fileFilters, cfilename))
751  continue;
752 
753  for (const ProjectConfiguration &p : projectConfigurationList) {
754 
755  if (!guiProject.checkVsConfigs.empty()) {
756  const bool doChecking = std::any_of(guiProject.checkVsConfigs.cbegin(), guiProject.checkVsConfigs.cend(), [&](const std::string& c) {
757  return c == p.configuration;
758  });
759  if (!doChecking)
760  continue;
761  }
762 
763  FileSettings fs{cfilename};
764  fs.cfg = p.name;
765  // TODO: detect actual MSC version
766  fs.msc = true;
767  fs.useMfc = useOfMfc;
768  fs.defines = "_WIN32=1";
769  if (p.platform == ProjectConfiguration::Win32)
770  fs.platformType = Platform::Type::Win32W;
771  else if (p.platform == ProjectConfiguration::x64) {
772  fs.platformType = Platform::Type::Win64;
773  fs.defines += ";_WIN64=1";
774  }
775  std::string additionalIncludePaths;
776  for (const ItemDefinitionGroup &i : itemDefinitionGroupList) {
777  if (!i.conditionIsTrue(p))
778  continue;
779  fs.standard = Standards::getCPP(i.cppstd);
780  fs.defines += ';' + i.preprocessorDefinitions;
781  if (i.enhancedInstructionSet == "StreamingSIMDExtensions")
782  fs.defines += ";__SSE__";
783  else if (i.enhancedInstructionSet == "StreamingSIMDExtensions2")
784  fs.defines += ";__SSE2__";
785  else if (i.enhancedInstructionSet == "AdvancedVectorExtensions")
786  fs.defines += ";__AVX__";
787  else if (i.enhancedInstructionSet == "AdvancedVectorExtensions2")
788  fs.defines += ";__AVX2__";
789  else if (i.enhancedInstructionSet == "AdvancedVectorExtensions512")
790  fs.defines += ";__AVX512__";
791  additionalIncludePaths += ';' + i.additionalIncludePaths;
792  }
793  fsSetDefines(fs, fs.defines);
794  fsSetIncludePaths(fs, Path::getPathFromFilename(filename), toStringList(includePath + ';' + additionalIncludePaths), variables);
795  fileSettings.push_back(std::move(fs));
796  }
797  }
798 
799  return true;
800 }
801 
802 bool ImportProject::importBcb6Prj(const std::string &projectFilename)
803 {
804  tinyxml2::XMLDocument doc;
805  const tinyxml2::XMLError error = doc.LoadFile(projectFilename.c_str());
806  if (error != tinyxml2::XML_SUCCESS) {
807  printError(std::string("Borland project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error));
808  return false;
809  }
810  const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
811  if (rootnode == nullptr) {
812  printError("Borland project file has no XML root node");
813  return false;
814  }
815 
816  const std::string& projectDir = Path::simplifyPath(Path::getPathFromFilename(projectFilename));
817 
818  std::list<std::string> compileList;
819  std::string includePath;
820  std::string userdefines;
821  std::string sysdefines;
822  std::string cflag1;
823 
824  for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) {
825  if (std::strcmp(node->Name(), "FILELIST") == 0) {
826  for (const tinyxml2::XMLElement *f = node->FirstChildElement(); f; f = f->NextSiblingElement()) {
827  if (std::strcmp(f->Name(), "FILE") == 0) {
828  const char *filename = f->Attribute("FILENAME");
829  if (filename && Path::acceptFile(filename))
830  compileList.emplace_back(filename);
831  }
832  }
833  } else if (std::strcmp(node->Name(), "MACROS") == 0) {
834  for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) {
835  if (std::strcmp(m->Name(), "INCLUDEPATH") == 0) {
836  const char *v = m->Attribute("value");
837  if (v)
838  includePath = v;
839  } else if (std::strcmp(m->Name(), "USERDEFINES") == 0) {
840  const char *v = m->Attribute("value");
841  if (v)
842  userdefines = v;
843  } else if (std::strcmp(m->Name(), "SYSDEFINES") == 0) {
844  const char *v = m->Attribute("value");
845  if (v)
846  sysdefines = v;
847  }
848  }
849  } else if (std::strcmp(node->Name(), "OPTIONS") == 0) {
850  for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) {
851  if (std::strcmp(m->Name(), "CFLAG1") == 0) {
852  const char *v = m->Attribute("value");
853  if (v)
854  cflag1 = v;
855  }
856  }
857  }
858  }
859 
860  std::set<std::string> cflags;
861 
862  // parse cflag1 and fill the cflags set
863  {
864  std::string arg;
865 
866  for (const char i : cflag1) {
867  if (i == ' ' && !arg.empty()) {
868  cflags.insert(arg);
869  arg.clear();
870  continue;
871  }
872  arg += i;
873  }
874 
875  if (!arg.empty()) {
876  cflags.insert(arg);
877  }
878 
879  // cleanup: -t is "An alternate name for the -Wxxx switches; there is no difference"
880  // -> Remove every known -txxx argument and replace it with its -Wxxx counterpart.
881  // This way, we know what we have to check for later on.
882  static const std::map<std::string, std::string> synonyms = {
883  { "-tC","-WC" },
884  { "-tCDR","-WCDR" },
885  { "-tCDV","-WCDV" },
886  { "-tW","-W" },
887  { "-tWC","-WC" },
888  { "-tWCDR","-WCDR" },
889  { "-tWCDV","-WCDV" },
890  { "-tWD","-WD" },
891  { "-tWDR","-WDR" },
892  { "-tWDV","-WDV" },
893  { "-tWM","-WM" },
894  { "-tWP","-WP" },
895  { "-tWR","-WR" },
896  { "-tWU","-WU" },
897  { "-tWV","-WV" }
898  };
899 
900  for (std::map<std::string, std::string>::const_iterator i = synonyms.cbegin(); i != synonyms.cend(); ++i) {
901  if (cflags.erase(i->first) > 0) {
902  cflags.insert(i->second);
903  }
904  }
905  }
906 
907  std::string predefines;
908  std::string cppPredefines;
909 
910  // Collecting predefines. See BCB6 help topic "Predefined macros"
911  {
912  cppPredefines +=
913  // Defined if you've selected C++ compilation; will increase in later releases.
914  // value 0x0560 (but 0x0564 for our BCB6 SP4)
915  // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros
916  ";__BCPLUSPLUS__=0x0560"
917 
918  // Defined if in C++ mode; otherwise, undefined.
919  ";__cplusplus=1"
920 
921  // Defined as 1 for C++ files(meaning that templates are supported); otherwise, it is undefined.
922  ";__TEMPLATES__=1"
923 
924  // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type.
925  ";_WCHAR_T"
926 
927  // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type.
928  ";_WCHAR_T_DEFINED"
929 
930  // Defined in any compiler that has an optimizer.
931  ";__BCOPT__=1"
932 
933  // Version number.
934  // BCB6 is 0x056X (SP4 is 0x0564)
935  // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros
936  ";__BORLANDC__=0x0560"
937  ";__TCPLUSPLUS__=0x0560"
938  ";__TURBOC__=0x0560";
939 
940  // Defined if Calling Convention is set to cdecl; otherwise undefined.
941  const bool useCdecl = (cflags.find("-p") == cflags.end()
942  && cflags.find("-pm") == cflags.end()
943  && cflags.find("-pr") == cflags.end()
944  && cflags.find("-ps") == cflags.end());
945  if (useCdecl)
946  predefines += ";__CDECL=1";
947 
948  // Defined by default indicating that the default char is unsigned char. Use the -K compiler option to undefine this macro.
949  const bool treatCharAsUnsignedChar = (cflags.find("-K") != cflags.end());
950  if (treatCharAsUnsignedChar)
951  predefines += ";_CHAR_UNSIGNED=1";
952 
953  // Defined whenever one of the CodeGuard compiler options is used; otherwise it is undefined.
954  const bool codeguardUsed = (cflags.find("-vGd") != cflags.end()
955  || cflags.find("-vGt") != cflags.end()
956  || cflags.find("-vGc") != cflags.end());
957  if (codeguardUsed)
958  predefines += ";__CODEGUARD__";
959 
960  // When defined, the macro indicates that the program is a console application.
961  const bool isConsoleApp = (cflags.find("-WC") != cflags.end());
962  if (isConsoleApp)
963  predefines += ";__CONSOLE__=1";
964 
965  // Enable stack unwinding. This is true by default; use -xd- to disable.
966  const bool enableStackUnwinding = (cflags.find("-xd-") == cflags.end());
967  if (enableStackUnwinding)
968  predefines += ";_CPPUNWIND=1";
969 
970  // Defined whenever the -WD compiler option is used; otherwise it is undefined.
971  const bool isDLL = (cflags.find("-WD") != cflags.end());
972  if (isDLL)
973  predefines += ";__DLL__=1";
974 
975  // Defined when compiling in 32-bit flat memory model.
976  // TODO: not sure how to switch to another memory model or how to read configuration from project file
977  predefines += ";__FLAT__=1";
978 
979  // Always defined. The default value is 300. You can change the value to 400 or 500 by using the /4 or /5 compiler options.
980  if (cflags.find("-6") != cflags.end())
981  predefines += ";_M_IX86=600";
982  else if (cflags.find("-5") != cflags.end())
983  predefines += ";_M_IX86=500";
984  else if (cflags.find("-4") != cflags.end())
985  predefines += ";_M_IX86=400";
986  else
987  predefines += ";_M_IX86=300";
988 
989  // Defined only if the -WM option is used. It specifies that the multithread library is to be linked.
990  const bool linkMtLib = (cflags.find("-WM") != cflags.end());
991  if (linkMtLib)
992  predefines += ";__MT__=1";
993 
994  // Defined if Calling Convention is set to Pascal; otherwise undefined.
995  const bool usePascalCallingConvention = (cflags.find("-p") != cflags.end());
996  if (usePascalCallingConvention)
997  predefines += ";__PASCAL__=1";
998 
999  // Defined if you compile with the -A compiler option; otherwise, it is undefined.
1000  const bool useAnsiKeywordExtensions = (cflags.find("-A") != cflags.end());
1001  if (useAnsiKeywordExtensions)
1002  predefines += ";__STDC__=1";
1003 
1004  // Thread Local Storage. Always true in C++Builder.
1005  predefines += ";__TLC__=1";
1006 
1007  // Defined for Windows-only code.
1008  const bool isWindowsTarget = (cflags.find("-WC") != cflags.end()
1009  || cflags.find("-WCDR") != cflags.end()
1010  || cflags.find("-WCDV") != cflags.end()
1011  || cflags.find("-WD") != cflags.end()
1012  || cflags.find("-WDR") != cflags.end()
1013  || cflags.find("-WDV") != cflags.end()
1014  || cflags.find("-WM") != cflags.end()
1015  || cflags.find("-WP") != cflags.end()
1016  || cflags.find("-WR") != cflags.end()
1017  || cflags.find("-WU") != cflags.end()
1018  || cflags.find("-WV") != cflags.end());
1019  if (isWindowsTarget)
1020  predefines += ";_Windows";
1021 
1022  // Defined for console and GUI applications.
1023  // TODO: I'm not sure about the difference to define "_Windows".
1024  // From description, I would assume __WIN32__ is only defined for
1025  // executables, while _Windows would also be defined for DLLs, etc.
1026  // However, in a newly created DLL project, both __WIN32__ and
1027  // _Windows are defined. -> treating them the same for now.
1028  // Also boost uses __WIN32__ for OS identification.
1029  const bool isConsoleOrGuiApp = isWindowsTarget;
1030  if (isConsoleOrGuiApp)
1031  predefines += ";__WIN32__=1";
1032  }
1033 
1034  // Include paths may contain variables like "$(BCB)\include" or "$(BCB)\include\vcl".
1035  // Those get resolved by ImportProject::FileSettings::setIncludePaths by
1036  // 1. checking the provided variables map ("BCB" => "C:\\Program Files (x86)\\Borland\\CBuilder6")
1037  // 2. checking env variables as a fallback
1038  // Setting env is always possible. Configuring the variables via cli might be an addition.
1039  // Reading the BCB6 install location from registry in windows environments would also be possible,
1040  // but I didn't see any such functionality around the source. Not in favor of adding it only
1041  // for the BCB6 project loading.
1042  std::map<std::string, std::string, cppcheck::stricmp> variables;
1043  const std::string defines = predefines + ";" + sysdefines + ";" + userdefines;
1044  const std::string cppDefines = cppPredefines + ";" + defines;
1045  const bool forceCppMode = (cflags.find("-P") != cflags.end());
1046 
1047  for (const std::string &c : compileList) {
1048  // C++ compilation is selected by file extension by default, so these
1049  // defines have to be configured on a per-file base.
1050  //
1051  // > Files with the .CPP extension compile as C++ files. Files with a .C
1052  // > extension, with no extension, or with extensions other than .CPP,
1053  // > .OBJ, .LIB, or .ASM compile as C files.
1054  // (http://docwiki.embarcadero.com/RADStudio/Tokyo/en/BCC32.EXE,_the_C%2B%2B_32-bit_Command-Line_Compiler)
1055  //
1056  // We can also force C++ compilation for all files using the -P command line switch.
1057  const bool cppMode = forceCppMode || Path::getFilenameExtensionInLowerCase(c) == ".cpp";
1058  FileSettings fs{Path::simplifyPath(Path::isAbsolute(c) ? c : projectDir + c)};
1059  fsSetIncludePaths(fs, projectDir, toStringList(includePath), variables);
1060  fsSetDefines(fs, cppMode ? cppDefines : defines);
1061  fileSettings.push_back(std::move(fs));
1062  }
1063 
1064  return true;
1065 }
1066 
1067 static std::string joinRelativePath(const std::string &path1, const std::string &path2)
1068 {
1069  if (!path1.empty() && !Path::isAbsolute(path2))
1070  return path1 + path2;
1071  return path2;
1072 }
1073 
1074 static std::list<std::string> readXmlStringList(const tinyxml2::XMLElement *node, const std::string &path, const char name[], const char attribute[])
1075 {
1076  std::list<std::string> ret;
1077  for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
1078  if (strcmp(child->Name(), name) != 0)
1079  continue;
1080  const char *attr = attribute ? child->Attribute(attribute) : child->GetText();
1081  if (attr)
1082  ret.push_back(joinRelativePath(path, attr));
1083  }
1084  return ret;
1085 }
1086 
1087 static std::string join(const std::list<std::string> &strlist, const char *sep)
1088 {
1089  std::string ret;
1090  for (const std::string &s : strlist) {
1091  ret += (ret.empty() ? "" : sep) + s;
1092  }
1093  return ret;
1094 }
1095 
1096 static std::string istream_to_string(std::istream &istr)
1097 {
1098  std::istreambuf_iterator<char> eos;
1099  return std::string(std::istreambuf_iterator<char>(istr), eos);
1100 }
1101 
1102 static const char * readSafe(const char *s, const char *def) {
1103  return s ? s : def;
1104 }
1105 
1106 bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *settings)
1107 {
1108  tinyxml2::XMLDocument doc;
1109  const std::string xmldata = istream_to_string(istr);
1110  const tinyxml2::XMLError error = doc.Parse(xmldata.data(), xmldata.size());
1111  if (error != tinyxml2::XML_SUCCESS) {
1112  printError(std::string("Cppcheck GUI project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error));
1113  return false;
1114  }
1115  const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
1116  if (rootnode == nullptr || strcmp(rootnode->Name(), CppcheckXml::ProjectElementName) != 0) {
1117  printError("Cppcheck GUI project file has no XML root node");
1118  return false;
1119  }
1120 
1121  const std::string &path = mPath;
1122 
1123  std::list<std::string> paths;
1124  std::list<SuppressionList::Suppression> suppressions;
1125  Settings temp;
1126 
1127  // default to --check-level=normal for import for now
1129 
1130  guiProject.analyzeAllVsConfigs.clear();
1131 
1132  bool checkLevelExhaustive = false;
1133 
1134  // TODO: this should support all available command-line options
1135  for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) {
1136  if (strcmp(node->Name(), CppcheckXml::RootPathName) == 0) {
1137  if (node->Attribute(CppcheckXml::RootPathNameAttrib)) {
1138  temp.basePaths.push_back(joinRelativePath(path, node->Attribute(CppcheckXml::RootPathNameAttrib)));
1139  temp.relativePaths = true;
1140  }
1141  } else if (strcmp(node->Name(), CppcheckXml::BuildDirElementName) == 0)
1142  temp.buildDir = joinRelativePath(path, readSafe(node->GetText(), ""));
1143  else if (strcmp(node->Name(), CppcheckXml::IncludeDirElementName) == 0)
1145  else if (strcmp(node->Name(), CppcheckXml::DefinesElementName) == 0)
1147  else if (strcmp(node->Name(), CppcheckXml::UndefinesElementName) == 0) {
1148  for (const std::string &u : readXmlStringList(node, "", CppcheckXml::UndefineName, nullptr))
1149  temp.userUndefs.insert(u);
1150  } else if (strcmp(node->Name(), CppcheckXml::ImportProjectElementName) == 0) {
1151  const std::string t_str = readSafe(node->GetText(), "");
1152  if (!t_str.empty())
1153  guiProject.projectFile = path + t_str;
1154  }
1155  else if (strcmp(node->Name(), CppcheckXml::PathsElementName) == 0)
1157  else if (strcmp(node->Name(), CppcheckXml::ExcludeElementName) == 0)
1159  else if (strcmp(node->Name(), CppcheckXml::FunctionContracts) == 0)
1160  ;
1161  else if (strcmp(node->Name(), CppcheckXml::VariableContractsElementName) == 0)
1162  ;
1163  else if (strcmp(node->Name(), CppcheckXml::IgnoreElementName) == 0)
1165  else if (strcmp(node->Name(), CppcheckXml::LibrariesElementName) == 0)
1166  guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr);
1167  else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0) {
1168  for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
1169  if (strcmp(child->Name(), CppcheckXml::SuppressionElementName) != 0)
1170  continue;
1172  s.errorId = readSafe(child->GetText(), "");
1173  s.fileName = readSafe(child->Attribute("fileName"), "");
1174  if (!s.fileName.empty())
1175  s.fileName = joinRelativePath(path, s.fileName);
1176  s.lineNumber = child->IntAttribute("lineNumber", SuppressionList::Suppression::NO_LINE);
1177  s.symbolName = readSafe(child->Attribute("symbolName"), "");
1178  s.hash = strToInt<std::size_t>(readSafe(child->Attribute("hash"), "0"));
1179  suppressions.push_back(std::move(s));
1180  }
1181  } else if (strcmp(node->Name(), CppcheckXml::VSConfigurationElementName) == 0)
1183  else if (strcmp(node->Name(), CppcheckXml::PlatformElementName) == 0)
1184  guiProject.platform = readSafe(node->GetText(), "");
1185  else if (strcmp(node->Name(), CppcheckXml::AnalyzeAllVsConfigsElementName) == 0)
1186  guiProject.analyzeAllVsConfigs = readSafe(node->GetText(), "");
1187  else if (strcmp(node->Name(), CppcheckXml::Parser) == 0)
1188  temp.clang = true;
1189  else if (strcmp(node->Name(), CppcheckXml::AddonsElementName) == 0) {
1190  const auto& addons = readXmlStringList(node, emptyString, CppcheckXml::AddonElementName, nullptr);
1191  temp.addons.insert(addons.cbegin(), addons.cend());
1192  }
1193  else if (strcmp(node->Name(), CppcheckXml::TagsElementName) == 0)
1194  node->Attribute(CppcheckXml::TagElementName); // FIXME: Write some warning
1195  else if (strcmp(node->Name(), CppcheckXml::ToolsElementName) == 0) {
1196  const std::list<std::string> toolList = readXmlStringList(node, emptyString, CppcheckXml::ToolElementName, nullptr);
1197  for (const std::string &toolName : toolList) {
1198  if (toolName == CppcheckXml::ClangTidy)
1199  temp.clangTidy = true;
1200  }
1201  } else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0)
1202  temp.checkHeaders = (strcmp(readSafe(node->GetText(), ""), "true") == 0);
1203  else if (strcmp(node->Name(), CppcheckXml::CheckLevelExhaustiveElementName) == 0)
1204  checkLevelExhaustive = true;
1205  else if (strcmp(node->Name(), CppcheckXml::CheckUnusedTemplatesElementName) == 0)
1206  temp.checkUnusedTemplates = (strcmp(readSafe(node->GetText(), ""), "true") == 0);
1207  else if (strcmp(node->Name(), CppcheckXml::MaxCtuDepthElementName) == 0)
1208  temp.maxCtuDepth = strToInt<int>(readSafe(node->GetText(), "2")); // TODO: bail out when missing?
1209  else if (strcmp(node->Name(), CppcheckXml::MaxTemplateRecursionElementName) == 0)
1210  temp.maxTemplateRecursion = strToInt<int>(readSafe(node->GetText(), "100")); // TODO: bail out when missing?
1211  else if (strcmp(node->Name(), CppcheckXml::CheckUnknownFunctionReturn) == 0)
1212  ; // TODO
1213  else if (strcmp(node->Name(), Settings::SafeChecks::XmlRootName) == 0) {
1214  for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
1215  if (strcmp(child->Name(), Settings::SafeChecks::XmlClasses) == 0)
1216  temp.safeChecks.classes = true;
1217  else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalFunctions) == 0)
1218  temp.safeChecks.externalFunctions = true;
1219  else if (strcmp(child->Name(), Settings::SafeChecks::XmlInternalFunctions) == 0)
1220  temp.safeChecks.internalFunctions = true;
1221  else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalVariables) == 0)
1222  temp.safeChecks.externalVariables = true;
1223  else {
1224  printError("Unknown '" + std::string(Settings::SafeChecks::XmlRootName) + "' element '" + std::string(child->Name()) + "' in Cppcheck project file");
1225  return false;
1226  }
1227  }
1228  } else if (strcmp(node->Name(), CppcheckXml::TagWarningsElementName) == 0)
1229  ; // TODO
1230  // Cppcheck Premium features
1231  else if (strcmp(node->Name(), CppcheckXml::BughuntingElementName) == 0)
1232  temp.premiumArgs += " --bughunting";
1233  else if (strcmp(node->Name(), CppcheckXml::CertIntPrecisionElementName) == 0)
1234  temp.premiumArgs += std::string(" --cert-c-int-precision=") + readSafe(node->GetText(), "0");
1235  else if (strcmp(node->Name(), CppcheckXml::CodingStandardsElementName) == 0) {
1236  for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
1237  if (strcmp(child->Name(), CppcheckXml::CodingStandardElementName) == 0 && child->GetText())
1238  temp.premiumArgs += std::string(" --") + child->GetText();
1239  }
1240  }
1241  else if (strcmp(node->Name(), CppcheckXml::ProjectNameElementName) == 0)
1242  ; // no-op
1243  else {
1244  printError("Unknown element '" + std::string(node->Name()) + "' in Cppcheck project file");
1245  return false;
1246  }
1247  }
1248  settings->basePaths = temp.basePaths;
1249  settings->relativePaths |= temp.relativePaths;
1250  settings->buildDir = temp.buildDir;
1251  settings->includePaths = temp.includePaths;
1252  settings->userDefines = temp.userDefines;
1253  settings->userUndefs = temp.userUndefs;
1254  settings->addons = temp.addons;
1255  settings->clang = temp.clang;
1256  settings->clangTidy = temp.clangTidy;
1257 
1258  if (!settings->premiumArgs.empty())
1259  settings->premiumArgs += temp.premiumArgs;
1260  else if (!temp.premiumArgs.empty())
1261  settings->premiumArgs = temp.premiumArgs.substr(1);
1262 
1263  for (const std::string &p : paths)
1264  guiProject.pathNames.push_back(p);
1265  settings->supprs.nomsg.addSuppressions(std::move(suppressions));
1266  settings->checkHeaders = temp.checkHeaders;
1267  settings->checkUnusedTemplates = temp.checkUnusedTemplates;
1268  settings->maxCtuDepth = temp.maxCtuDepth;
1269  settings->maxTemplateRecursion = temp.maxTemplateRecursion;
1270  settings->safeChecks = temp.safeChecks;
1271 
1272  if (checkLevelExhaustive)
1273  settings->setCheckLevel(Settings::CheckLevel::exhaustive);
1274  else
1275  settings->setCheckLevel(Settings::CheckLevel::normal);
1276 
1277  return true;
1278 }
1279 
1281 {
1282  std::set<std::string> filenames;
1283  for (std::list<FileSettings>::iterator it = fileSettings.begin(); it != fileSettings.end();) {
1284  if (it->cfg.empty()) {
1285  ++it;
1286  continue;
1287  }
1288  const FileSettings &fs = *it;
1289  bool remove = false;
1290  if (!startsWith(fs.cfg,"Debug"))
1291  remove = true;
1292  if (platform == Platform::Type::Win64 && fs.platformType != platform)
1293  remove = true;
1294  else if ((platform == Platform::Type::Win32A || platform == Platform::Type::Win32W) && fs.platformType == Platform::Type::Win64)
1295  remove = true;
1296  else if (filenames.find(fs.filename()) != filenames.end())
1297  remove = true;
1298  if (remove) {
1299  it = fileSettings.erase(it);
1300  } else {
1301  filenames.insert(fs.filename());
1302  ++it;
1303  }
1304  }
1305 }
1306 
1307 void ImportProject::selectVsConfigurations(Platform::Type platform, const std::vector<std::string> &configurations)
1308 {
1309  for (std::list<FileSettings>::iterator it = fileSettings.begin(); it != fileSettings.end();) {
1310  if (it->cfg.empty()) {
1311  ++it;
1312  continue;
1313  }
1314  const FileSettings &fs = *it;
1315  const auto config = fs.cfg.substr(0, fs.cfg.find('|'));
1316  bool remove = false;
1317  if (std::find(configurations.begin(), configurations.end(), config) == configurations.end())
1318  remove = true;
1319  if (platform == Platform::Type::Win64 && fs.platformType != platform)
1320  remove = true;
1321  else if ((platform == Platform::Type::Win32A || platform == Platform::Type::Win32W) && fs.platformType == Platform::Type::Win64)
1322  remove = true;
1323  if (remove) {
1324  it = fileSettings.erase(it);
1325  } else {
1326  ++it;
1327  }
1328  }
1329 }
1330 
1331 std::list<std::string> ImportProject::getVSConfigs()
1332 {
1333  return std::list<std::string>(mAllVSConfigs.cbegin(), mAllVSConfigs.cend());
1334 }
1335 
1336 void ImportProject::setRelativePaths(const std::string &filename)
1337 {
1338  if (Path::isAbsolute(filename))
1339  return;
1340  const std::vector<std::string> basePaths{Path::fromNativeSeparators(Path::getCurrentPath())};
1341  for (auto &fs: fileSettings) {
1342  fs.file = FileWithDetails{Path::getRelativePath(fs.filename(), basePaths)};
1343  for (auto &includePath: fs.includePaths)
1344  includePath = Path::getRelativePath(includePath, basePaths);
1345  }
1346 }
1347 
1348 void ImportProject::printError(const std::string &message)
1349 {
1350  std::cout << "cppcheck: error: " << message << std::endl;
1351 }
1352 
1353 bool ImportProject::sourceFileExists(const std::string &file)
1354 {
1355  return Path::isFile(file);
1356 }
static void fsParseCommand(FileSettings &fs, const std::string &command)
static void printError(const std::string &message)
std::list< FileSettings > fileSettings
Definition: importproject.h:70
void selectVsConfigurations(Platform::Type platform, const std::vector< std::string > &configurations)
void setRelativePaths(const std::string &filename)
std::string mPath
struct ImportProject::@6 guiProject
bool importBcb6Prj(const std::string &projectFilename)
void ignorePaths(const std::vector< std::string > &ipaths)
virtual bool sourceFileExists(const std::string &file)
bool importCppcheckGuiProject(std::istream &istr, Settings *settings)
void selectOneVsConfig(Platform::Type platform)
static void fsSetIncludePaths(FileSettings &fs, const std::string &basepath, const std::list< std::string > &in, std::map< std::string, std::string, cppcheck::stricmp > &variables)
std::list< std::string > getVSConfigs()
bool importVcxproj(const std::string &filename, std::map< std::string, std::string, cppcheck::stricmp > &variables, const std::string &additionalIncludeDirectories, const std::vector< std::string > &fileFilters)
static void fsSetDefines(FileSettings &fs, std::string defs)
void ignoreOtherConfigs(const std::string &cfg)
std::string platform
Definition: importproject.h:91
std::set< std::string > mAllVSConfigs
Type import(const std::string &filename, Settings *settings=nullptr)
bool importCompileCommands(std::istream &istr)
bool importSln(std::istream &istr, const std::string &path, const std::vector< std::string > &fileFilters)
static std::string simplifyPath(std::string originalPath)
Simplify path "foo/bar/.." => "foo".
Definition: path.cpp:83
static bool isFile(const std::string &path)
Checks if given path is a file.
Definition: path.cpp:329
static std::string fromNativeSeparators(std::string path)
Convert path to use internal path separators.
Definition: path.cpp:75
static std::string getCurrentPath()
Returns the absolute path of current working directory.
Definition: path.cpp:129
static std::string toNativeSeparators(std::string path)
Convert path to use native separators.
Definition: path.cpp:62
static std::string getPathFromFilename(const std::string &filename)
Lookup the path part from a filename (e.g., '/tmp/a.h' -> '/tmp/', 'a.h' -> '')
Definition: path.cpp:88
static std::string getAbsoluteFilePath(const std::string &filePath)
Get an absolute file path from a relative one.
Definition: path.cpp:284
static bool acceptFile(const std::string &filename)
Check if the file extension indicates that it's a C/C++ source file.
Definition: path.h:142
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
static std::string getFilenameExtensionInLowerCase(const std::string &path)
Get an extension of the filename in lower case.
Definition: path.cpp:124
static bool isAbsolute(const std::string &path)
Check if given path is absolute.
Definition: path.cpp:166
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 checkHeaders
Check code in the headers, this is on by default but can be turned off to save CPU.
Definition: settings.h:132
std::vector< std::string > basePaths
Paths used as base for conversion to relative paths.
Definition: settings.h:118
int maxCtuDepth
–max-ctu-depth
Definition: settings.h:247
bool relativePaths
Use relative paths in output.
Definition: settings.h:285
std::unordered_set< std::string > addons
addons, either filename of python/json file or json data
Definition: settings.h:109
SafeChecks safeChecks
Definition: settings.h:356
bool clang
Use Clang.
Definition: settings.h:150
std::string buildDir
–cppcheck-build-dir.
Definition: settings.h:121
std::string userDefines
defines given by the user
Definition: settings.h:386
std::string premiumArgs
Extra arguments for Cppcheck Premium addon.
Definition: settings.h:273
void setCheckLevel(CheckLevel level)
Definition: settings.cpp:288
bool clangTidy
Use clang-tidy.
Definition: settings.h:156
std::list< std::string > includePaths
List of include paths, e.g.
Definition: settings.h:224
int maxTemplateRecursion
max template recursion
Definition: settings.h:250
bool checkUnusedTemplates
Check unused/uninstantiated templates.
Definition: settings.h:147
std::vector< std::string > fileFilters
List of –file-filter for analyzing special files.
Definition: settings.h:217
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
Token * next()
Definition: token.h:830
static const std::string emptyString
Definition: config.h:127
@ error
Programming error.
static void loadVisualStudioProperties(const std::string &props, std::map< std::string, std::string, cppcheck::stricmp > &variables, std::string &includePath, const std::string &additionalIncludeDirectories, std::list< ItemDefinitionGroup > &itemDefinitionGroupList)
static std::string join(const std::list< std::string > &strlist, const char *sep)
static bool simplifyPathWithVariables(std::string &s, std::map< std::string, std::string, cppcheck::stricmp > &variables)
static std::string istream_to_string(std::istream &istr)
static const char * readSafe(const char *s, const char *def)
static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[])
static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map< std::string, std::string, cppcheck::stricmp > &variables, std::string &includePath, bool *useOfMfc)
static std::string unescape(const std::string &in)
static std::list< std::string > readXmlStringList(const tinyxml2::XMLElement *node, const std::string &path, const char name[], const char attribute[])
static std::string joinRelativePath(const std::string &path1, const std::string &path2)
static std::list< std::string > toStringList(const std::string &s)
static constexpr char RootPathName[]
static constexpr char VSConfigurationName[]
static constexpr char DirElementName[]
static constexpr char DefineName[]
static constexpr char ExcludePathNameAttrib[]
static constexpr char PathsElementName[]
static constexpr char VariableContractsElementName[]
static constexpr char FunctionContracts[]
static constexpr char DefinesElementName[]
static constexpr char LibrariesElementName[]
static constexpr char ToolsElementName[]
static constexpr char IncludeDirElementName[]
static constexpr char ExcludeElementName[]
static constexpr char ProjectElementName[]
static constexpr char CertIntPrecisionElementName[]
static constexpr char PlatformElementName[]
static constexpr char TagsElementName[]
static constexpr char RootPathNameAttrib[]
static constexpr char CodingStandardElementName[]
static constexpr char IgnorePathNameAttrib[]
static constexpr char CodingStandardsElementName[]
static constexpr char AddonsElementName[]
static constexpr char UndefinesElementName[]
static constexpr char ImportProjectElementName[]
static constexpr char Parser[]
static constexpr char BughuntingElementName[]
static constexpr char AddonElementName[]
static constexpr char CheckUnknownFunctionReturn[]
static constexpr char TagWarningsElementName[]
static constexpr char DefineNameAttrib[]
static constexpr char AnalyzeAllVsConfigsElementName[]
static constexpr char TagElementName[]
static constexpr char ProjectNameElementName[]
static constexpr char BuildDirElementName[]
static constexpr char MaxTemplateRecursionElementName[]
static constexpr char ClangTidy[]
static constexpr char ExcludePathName[]
static constexpr char CheckUnusedTemplatesElementName[]
static constexpr char VSConfigurationElementName[]
static constexpr char CheckLevelExhaustiveElementName[]
static constexpr char DirNameAttrib[]
static constexpr char MaxCtuDepthElementName[]
static constexpr char UndefineName[]
static constexpr char SuppressionElementName[]
static constexpr char IgnoreElementName[]
static constexpr char IgnorePathName[]
static constexpr char ToolElementName[]
static constexpr char SuppressionsElementName[]
static constexpr char CheckHeadersElementName[]
static constexpr char PathName[]
static constexpr char PathNameAttrib[]
static constexpr char LibraryElementName[]
static std::string cfg(const std::vector< std::string > &configs, const std::string &userDefines)
bool conditionIsTrue(const Token *condition, ProgramMemory pm, const Settings &settings)
Is condition always true when variable has given value?
File settings.
Definition: filesettings.h:57
std::string defines
Definition: filesettings.h:72
const std::string & filename() const
Definition: filesettings.h:68
std::set< std::string > undefs
Definition: filesettings.h:77
std::string cfg
Definition: filesettings.h:66
Platform::Type platformType
Definition: filesettings.h:82
std::string standard
Definition: filesettings.h:81
std::list< std::string > includePaths
Definition: filesettings.h:78
std::list< std::string > systemIncludePaths
Definition: filesettings.h:80
bool classes
Public interface of classes.
Definition: settings.h:334
static const char XmlExternalFunctions[]
Definition: settings.h:320
static const char XmlInternalFunctions[]
Definition: settings.h:321
bool externalVariables
Global variables that can be modified outside the TU.
Definition: settings.h:353
static const char XmlClasses[]
Definition: settings.h:319
static const char XmlRootName[]
Definition: settings.h:318
static const char XmlExternalVariables[]
Definition: settings.h:322
bool externalFunctions
External functions.
Definition: settings.h:341
bool internalFunctions
Experimental: assume that internal functions can be used in any way This is only available in the GUI...
Definition: settings.h:347
std::string getCPP() const
Definition: standards.h:103
cppstd_t
C++ code standard.
Definition: standards.h:43
@ CPPLatest
Definition: standards.h:43
bool matchglob(const std::string &pattern, const std::string &name)
Definition: utils.cpp:54
bool isValidGlobPattern(const std::string &pattern)
Definition: utils.cpp:41
bool matchglobs(const std::vector< std::string > &patterns, const std::string &name)
Definition: utils.cpp:118
bool startsWith(const std::string &str, const char start[], std::size_t startlen)
Definition: utils.h:94
bool endsWith(const std::string &str, char c)
Definition: utils.h:110