Skip to content

Commit 87008b0

Browse files
committed
Add support for visual Studio 2026 .slnx project files
1 parent 8119a9d commit 87008b0

6 files changed

Lines changed: 78 additions & 11 deletions

File tree

cli/cmdlineparser.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
11881188
}
11891189
if (projectType == ImportProject::Type::COMPILE_DB)
11901190
mSettings.maxConfigsProject = 1;
1191-
if (projectType == ImportProject::Type::VS_SLN || projectType == ImportProject::Type::VS_VCXPROJ) {
1191+
if (projectType == ImportProject::Type::VS_SLN ||
1192+
projectType == ImportProject::Type::VS_SLNX ||
1193+
projectType == ImportProject::Type::VS_VCXPROJ) {
11921194
mSettings.libraries.emplace_back("windows");
11931195
}
11941196
for (const auto &error : project.errors)
@@ -1214,7 +1216,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
12141216
mLogger.printError("--project-configuration parameter is empty.");
12151217
return Result::Fail;
12161218
}
1217-
if (projectType != ImportProject::Type::VS_SLN && projectType != ImportProject::Type::VS_VCXPROJ) {
1219+
if (projectType != ImportProject::Type::VS_SLN &&
1220+
projectType != ImportProject::Type::VS_SLNX &&
1221+
projectType != ImportProject::Type::VS_VCXPROJ) {
12181222
mLogger.printError("--project-configuration has no effect - no Visual Studio project provided.");
12191223
return Result::Fail;
12201224
}
@@ -1649,7 +1653,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
16491653
mSettings.platform.defaultSign = defaultSign;
16501654

16511655
if (!mSettings.analyzeAllVsConfigs) {
1652-
if (projectType != ImportProject::Type::VS_SLN && projectType != ImportProject::Type::VS_VCXPROJ) {
1656+
if (projectType != ImportProject::Type::VS_SLN &&
1657+
projectType != ImportProject::Type::VS_SLNX &&
1658+
projectType != ImportProject::Type::VS_VCXPROJ) {
16531659
if (mAnalyzeAllVsConfigsSetOnCmdLine) {
16541660
mLogger.printError("--no-analyze-all-vs-configs has no effect - no Visual Studio project provided.");
16551661
return Result::Fail;
@@ -1935,13 +1941,13 @@ void CmdLineParser::printHelp() const
19351941

19361942
oss <<
19371943
" --project=<file> Run Cppcheck on project. The <file> can be a Visual\n"
1938-
" Studio Solution (*.sln), Visual Studio Project\n"
1944+
" Studio Solution (*.sln) or (*.slnx), Visual Studio Project\n"
19391945
" (*.vcxproj), compile database (compile_commands.json),\n"
19401946
" or Borland C++ Builder 6 (*.bpr). The files to analyse,\n"
19411947
" include paths, defines, platform and undefines in\n"
19421948
" the specified file will be used.\n"
19431949
" --project-configuration=<config>\n"
1944-
" If used together with a Visual Studio Solution (*.sln)\n"
1950+
" If used together with a Visual Studio Solution (*.sln) or (*.slnx)\n"
19451951
" or Visual Studio Project (*.vcxproj) you can limit\n"
19461952
" the configuration cppcheck should check.\n"
19471953
" For example: '--project-configuration=Release|Win32'\n"

gui/mainwindow.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ QStringList MainWindow::selectFilesToAnalyze(QFileDialog::FileMode mode)
768768
QMap<QString,QString> filters;
769769
filters[tr("C/C++ Source")] = FileList::getDefaultFilters().join(" ");
770770
filters[tr("Compile database")] = compile_commands_json;
771-
filters[tr("Visual Studio")] = "*.sln *.vcxproj";
771+
filters[tr("Visual Studio")] = "*.sln *.slnx *.vcxproj";
772772
filters[tr("Borland C++ Builder 6")] = "*.bpr";
773773
QString lastFilter = mSettings->value(SETTINGS_LAST_ANALYZE_FILES_FILTER).toString();
774774
selected = QFileDialog::getOpenFileNames(this,
@@ -811,13 +811,14 @@ void MainWindow::analyzeFiles()
811811

812812
const QString file0 = (!selected.empty() ? selected[0].toLower() : QString());
813813
if (file0.endsWith(".sln")
814+
|| file0.endsWith(".slnx")
814815
|| file0.endsWith(".vcxproj")
815816
|| file0.endsWith(compile_commands_json)
816817
|| file0.endsWith(".bpr")) {
817818
ImportProject p;
818819
p.import(selected[0].toStdString());
819820

820-
if (file0.endsWith(".sln")) {
821+
if (file0.endsWith(".sln") || file0.endsWith(".slnx")) {
821822
QStringList configs;
822823
for (auto it = p.fileSettings.cbegin(); it != p.fileSettings.cend(); ++it) {
823824
const QString cfg(QString::fromStdString(it->cfg));
@@ -1968,6 +1969,7 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile, const QStringLis
19681969
switch (result) {
19691970
case ImportProject::Type::COMPILE_DB:
19701971
case ImportProject::Type::VS_SLN:
1972+
case ImportProject::Type::VS_SLNX:
19711973
case ImportProject::Type::VS_VCXPROJ:
19721974
case ImportProject::Type::BORLAND:
19731975
case ImportProject::Type::CPPCHECK_GUI:

gui/projectfiledialog.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ static std::string suppressionAsText(const SuppressionList::Suppression& s)
111111

112112
QStringList ProjectFileDialog::getProjectConfigs(const QString &fileName)
113113
{
114-
if (!fileName.endsWith(".sln") && !fileName.endsWith(".vcxproj"))
114+
if (!fileName.endsWith(".sln") && !fileName.endsWith(".slnx") && !fileName.endsWith(".vcxproj"))
115115
return QStringList();
116116
QStringList ret;
117117
ImportProject importer;
@@ -596,7 +596,7 @@ void ProjectFileDialog::updatePathsAndDefines()
596596
{
597597
const QString &fileName = mUI->mEditImportProject->text();
598598
const bool importProject = !fileName.isEmpty();
599-
const bool hasConfigs = fileName.endsWith(".sln") || fileName.endsWith(".vcxproj");
599+
const bool hasConfigs = fileName.endsWith(".sln") || fileName.endsWith(".slnx") || fileName.endsWith(".vcxproj");
600600
mUI->mBtnClearImportProject->setEnabled(importProject);
601601
mUI->mListCheckPaths->setEnabled(!importProject);
602602
mUI->mListIncludeDirs->setEnabled(!importProject);
@@ -626,7 +626,7 @@ void ProjectFileDialog::browseImportProject()
626626
const QFileInfo inf(mProjectFile->getFilename());
627627
const QDir &dir = inf.absoluteDir();
628628
QMap<QString,QString> filters;
629-
filters[tr("Visual Studio")] = "*.sln *.vcxproj";
629+
filters[tr("Visual Studio")] = "*.sln *.slnx *.vcxproj";
630630
filters[tr("Compile database")] = "compile_commands.json";
631631
filters[tr("Borland C++ Builder 6")] = "*.bpr";
632632
QString fileName = QFileDialog::getOpenFileName(this, tr("Import Project"),

lib/importproject.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,11 @@ ImportProject::Type ImportProject::import(const std::string &filename, Settings
330330
setRelativePaths(filename);
331331
return ImportProject::Type::VS_SLN;
332332
}
333+
} else if (endsWith(filename, ".slnx")) {
334+
if (importSlnx(filename, fileFilters)) {
335+
setRelativePaths(filename);
336+
return ImportProject::Type::VS_SLNX;
337+
}
333338
} else if (endsWith(filename, ".vcxproj")) {
334339
std::map<std::string, std::string, cppcheck::stricmp> variables;
335340
std::vector<SharedItemsProject> sharedItemsProjects;
@@ -503,6 +508,54 @@ bool ImportProject::importSln(std::istream &istr, const std::string &path, const
503508
return true;
504509
}
505510

511+
bool ImportProject::importSlnx(const std::string& filename, const std::vector<std::string>& fileFilters)
512+
{
513+
tinyxml2::XMLDocument doc;
514+
const tinyxml2::XMLError error = doc.LoadFile(filename.c_str());
515+
if (error != tinyxml2::XML_SUCCESS) {
516+
errors.emplace_back(std::string("Visual Studio project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error));
517+
return false;
518+
}
519+
520+
const tinyxml2::XMLElement* const rootnode = doc.FirstChildElement();
521+
if (rootnode == nullptr) {
522+
errors.emplace_back("Visual Studio project file has no XML root node");
523+
return false;
524+
}
525+
526+
std::map<std::string, std::string, cppcheck::stricmp> variables;
527+
variables["SolutionDir"] = Path::simplifyPath(Path::getPathFromFilename(filename));
528+
529+
bool found = false;
530+
std::vector<SharedItemsProject> sharedItemsProjects;
531+
532+
for (const tinyxml2::XMLElement* node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) {
533+
const char* name = node->Name();
534+
if (std::strcmp(name, "Project") == 0) {
535+
const char* labelAttribute = node->Attribute("Path");
536+
if (labelAttribute) {
537+
std::string vcxproj(labelAttribute);
538+
vcxproj = Path::toNativeSeparators(std::move(vcxproj));
539+
if (!Path::isAbsolute(vcxproj))
540+
vcxproj = variables["SolutionDir"] + vcxproj;
541+
vcxproj = Path::fromNativeSeparators(std::move(vcxproj));
542+
if (!importVcxproj(vcxproj, variables, "", fileFilters, sharedItemsProjects)) {
543+
errors.emplace_back("failed to load '" + vcxproj + "' from Visual Studio solution");
544+
return false;
545+
}
546+
found = true;
547+
}
548+
}
549+
}
550+
551+
if (!found) {
552+
errors.emplace_back("no projects found in Visual Studio solution file");
553+
return false;
554+
}
555+
556+
return true;
557+
}
558+
506559
namespace {
507560
struct ProjectConfiguration {
508561
explicit ProjectConfiguration(const tinyxml2::XMLElement *cfg) {

lib/importproject.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class CPPCHECKLIB WARN_UNUSED ImportProject {
6363
FAILURE,
6464
COMPILE_DB,
6565
VS_SLN,
66+
VS_SLNX,
6667
VS_VCXPROJ,
6768
BORLAND,
6869
CPPCHECK_GUI
@@ -120,6 +121,7 @@ class CPPCHECKLIB WARN_UNUSED ImportProject {
120121
void setRelativePaths(const std::string &filename);
121122

122123
bool importSln(std::istream &istr, const std::string &path, const std::vector<std::string> &fileFilters);
124+
bool importSlnx(const std::string& filename, const std::vector<std::string>& fileFilters);
123125
SharedItemsProject importVcxitems(const std::string &filename, const std::vector<std::string> &fileFilters, std::vector<SharedItemsProject> &cache);
124126
bool importBcb6Prj(const std::string &projectFilename);
125127

man/manual.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,12 +309,16 @@ To ignore certain folders you can use `-i`. This will skip analysis of source fi
309309

310310
## Visual Studio
311311

312-
You can run Cppcheck on individual project files (\*.vcxproj) or on a whole solution (\*.sln)
312+
You can run Cppcheck on individual project files (\*.vcxproj) or on a whole solution (\*.sln) or (\*.slnx)
313313

314314
Running Cppcheck on an entire Visual Studio solution:
315315

316316
cppcheck --project=foobar.sln
317317

318+
Running Cppcheck on an entire Visual Studio 2026 solution:
319+
320+
cppcheck --project=foobar.slnx
321+
318322
Running Cppcheck on a Visual Studio project:
319323

320324
cppcheck --project=foobar.vcxproj

0 commit comments

Comments
 (0)