diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 086ac280971..e7679a70c0c 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -441,7 +441,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a bool def = false; bool maxconfigs = false; + ImportProject::Type projectType = ImportProject::Type::NONE; ImportProject project; + std::string vsConfig; bool executorAuto = true; @@ -1160,7 +1162,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a // --project else if (std::strncmp(argv[i], "--project=", 10) == 0) { - if (project.projectType != ImportProject::Type::NONE) + if (projectType != ImportProject::Type::NONE) { mLogger.printError("multiple --project options are not supported."); return Result::Fail; @@ -1168,9 +1170,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a mSettings.checkAllConfigurations = false; // Can be overridden with --max-configs or --force std::string projectFile = argv[i]+10; - ImportProject::Type projType = project.import(projectFile, &mSettings, &mSuppressions); - project.projectType = projType; - if (projType == ImportProject::Type::CPPCHECK_GUI) { + projectType = project.import(projectFile, &mSettings, &mSuppressions); + if (projectType == ImportProject::Type::CPPCHECK_GUI) { for (const std::string &lib : project.guiProject.libraries) mSettings.libraries.emplace_back(lib); @@ -1193,27 +1194,27 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a if (!projectFileGui.empty()) { // read underlying project projectFile = projectFileGui; - projType = project.import(projectFileGui, &mSettings, &mSuppressions); - if (projType == ImportProject::Type::CPPCHECK_GUI) { + projectType = project.import(projectFileGui, &mSettings, &mSuppressions); + if (projectType == ImportProject::Type::CPPCHECK_GUI) { mLogger.printError("nested Cppcheck GUI projects are not supported."); return Result::Fail; } } } - if (projType == ImportProject::Type::VS_SLN || projType == ImportProject::Type::VS_VCXPROJ) { + if (projectType == ImportProject::Type::VS_SLN || projectType == ImportProject::Type::VS_VCXPROJ) { if (project.guiProject.analyzeAllVsConfigs == "false") project.selectOneVsConfig(mSettings.platform.type); mSettings.libraries.emplace_back("windows"); } - if (projType == ImportProject::Type::MISSING) { + if (projectType == ImportProject::Type::MISSING) { mLogger.printError("failed to open project '" + projectFile + "'. The file does not exist."); return Result::Fail; } - if (projType == ImportProject::Type::UNKNOWN) { + if (projectType == ImportProject::Type::UNKNOWN) { mLogger.printError("failed to load project '" + projectFile + "'. The format is unknown."); return Result::Fail; } - if (projType == ImportProject::Type::FAILURE) { + if (projectType == ImportProject::Type::FAILURE) { mLogger.printError("failed to load project '" + projectFile + "'. An error occurred."); return Result::Fail; } @@ -1221,10 +1222,15 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a // --project-configuration else if (std::strncmp(argv[i], "--project-configuration=", 24) == 0) { - mVSConfig = argv[i] + 24; - // TODO: provide error when this does nothing - if (!mVSConfig.empty() && (project.projectType == ImportProject::Type::VS_SLN || project.projectType == ImportProject::Type::VS_VCXPROJ)) - project.ignoreOtherConfigs(mVSConfig); + vsConfig = argv[i] + 24; + if (vsConfig.empty()) { + mLogger.printError("--project-configuration parameter is empty."); + return Result::Fail; + } + if (projectType != ImportProject::Type::VS_SLN && projectType != ImportProject::Type::VS_VCXPROJ) { + mLogger.printError("--project-configuration has no effect - no Visual Studio project provided."); + return Result::Fail; + } } // Only print something when there are errors @@ -1594,11 +1600,15 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a //mLogger.printMessage("whole program analysis requires --cppcheck-build-dir to be active with -j."); } - if (!mPathNames.empty() && project.projectType != ImportProject::Type::NONE) { + if (!mPathNames.empty() && projectType != ImportProject::Type::NONE) { mLogger.printError("--project cannot be used in conjunction with source files."); return Result::Fail; } + if (!vsConfig.empty()) { + project.ignoreOtherConfigs(vsConfig); + } + if (!mSettings.buildDir.empty() && !Path::isDirectory(mSettings.buildDir)) { mLogger.printError("Directory '" + mSettings.buildDir + "' specified by --cppcheck-build-dir argument has to be existent."); return Result::Fail; diff --git a/cli/cmdlineparser.h b/cli/cmdlineparser.h index b1abdd1222b..8ec20f84d8f 100644 --- a/cli/cmdlineparser.h +++ b/cli/cmdlineparser.h @@ -164,7 +164,6 @@ class CmdLineParser { std::vector mIgnoredPaths; Settings &mSettings; Suppressions &mSuppressions; - std::string mVSConfig; }; /// @} diff --git a/lib/importproject.h b/lib/importproject.h index 0c2f3f8486b..82a36cac234 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -70,7 +70,6 @@ class CPPCHECKLIB WARN_UNUSED ImportProject { static void fsSetIncludePaths(FileSettings& fs, const std::string &basepath, const std::list &in, std::map &variables); std::list fileSettings; - Type projectType{Type::NONE}; ImportProject() = default; virtual ~ImportProject() = default; diff --git a/test/cli/helloworld_test.py b/test/cli/helloworld_test.py index 91546a496f9..bef1456af6b 100644 --- a/test/cli/helloworld_test.py +++ b/test/cli/helloworld_test.py @@ -7,6 +7,8 @@ import json import xml.etree.ElementTree as ET +import pytest + from testutils import create_gui_project_file, cppcheck __script_dir = os.path.dirname(os.path.abspath(__file__)) @@ -145,16 +147,27 @@ def test_basepath_absolute_path(): assert ret == 0, stdout assert stderr == '[main.c:5]: (error) Division by zero.\n' -def test_vs_project_local_path(): +def __test_vs_project_local_path(extra_args=None, exp_vs_cfg='Debug|Win32 Debug|x64 Release|Win32 Release|x64'): args = [ '--template=cppcheck1', '--project=helloworld.vcxproj' ] + if extra_args: + args += extra_args ret, stdout, stderr = cppcheck(args, cwd=__proj_dir) assert ret == 0, stdout - assert __getVsConfigs(stdout, 'main.c') == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' + assert __getVsConfigs(stdout, 'main.c') == exp_vs_cfg assert stderr == '[main.c:5]: (error) Division by zero.\n' +def test_vs_project_local_path(): + __test_vs_project_local_path() + +def test_vs_project_local_path_select_one(): + __test_vs_project_local_path(['--project-configuration=Release|Win32'], 'Release|Win32') + +def test_vs_project_local_path_select_one_multiple(): + __test_vs_project_local_path(['--project-configuration=Debug|Win32', '--project-configuration=Release|Win32'], 'Release|Win32') + def test_vs_project_relative_path(): args = [ '--template=cppcheck1', @@ -177,17 +190,30 @@ def test_vs_project_absolute_path(): assert __getVsConfigs(stdout, filename) == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' assert stderr == '[%s:5]: (error) Division by zero.\n' % filename -def test_cppcheck_project_local_path(): +def __test_cppcheck_project_local_path(extra_args=None, exp_vs_cfg='Debug|x64'): args = [ '--template=cppcheck1', '--platform=win64', '--project=helloworld.cppcheck' ] + if extra_args: + args += extra_args ret, stdout, stderr = cppcheck(args, cwd=__proj_dir) assert ret == 0, stdout - assert __getVsConfigs(stdout, 'main.c') == 'Debug|x64' + assert __getVsConfigs(stdout, 'main.c') == exp_vs_cfg assert stderr == '[main.c:5]: (error) Division by zero.\n' +def test_cppcheck_project_local_path(): + __test_cppcheck_project_local_path() + +@pytest.mark.xfail # TODO: no source files found +def test_cppcheck_project_local_path_select_one(): + __test_cppcheck_project_local_path(['--project-configuration=Release|Win32'], 'Release|Win32') + +@pytest.mark.xfail # TODO: no source files found +def test_cppcheck_project_local_path_select_one_multiple(): + __test_cppcheck_project_local_path(['--project-configuration=Debug|Win32', '--project-configuration=Release|Win32'], 'Release|Win32') + def test_cppcheck_project_relative_path(): args = [ '--template=cppcheck1', diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 8818d83fabe..3d066e3bb77 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -450,6 +450,8 @@ class TestCmdlineParser : public TestFixture { TEST_CASE(noCheckUnusedTemplates); TEST_CASE(clangTidy); TEST_CASE(clangTidyCustom); + TEST_CASE(projectConfigurationNoProject); + TEST_CASE(projectConfigurationEmpty); TEST_CASE(ignorepaths1); TEST_CASE(ignorepaths2); @@ -3066,6 +3068,20 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS("clang-tidy-14", settings->clangTidyExecutable); } + void projectConfigurationNoProject() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--project-configuration=Debug|Win32", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parseFromArgs(argv)); + ASSERT_EQUALS("cppcheck: error: --project-configuration has no effect - no Visual Studio project provided.\n", logger->str()); + } + + void projectConfigurationEmpty() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--project-configuration=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parseFromArgs(argv)); + ASSERT_EQUALS("cppcheck: error: --project-configuration parameter is empty.\n", logger->str()); + } + void ignorepaths1() { REDIRECT; const char * const argv[] = {"cppcheck", "-isrc", "file.cpp"};