diff --git a/.github/workflows/selfcheck.yml b/.github/workflows/selfcheck.yml index 13e70b56668..61cc1463274 100644 --- a/.github/workflows/selfcheck.yml +++ b/.github/workflows/selfcheck.yml @@ -121,7 +121,7 @@ jobs: - name: Self check (unusedFunction / no test / no gui) run: | - supprs="--suppress=unusedFunction:lib/errorlogger.h:193 --suppress=unusedFunction:lib/importproject.cpp:1508 --suppress=unusedFunction:lib/importproject.cpp:1532" + supprs="--suppress=unusedFunction:lib/errorlogger.h:193 --suppress=unusedFunction:lib/importproject.cpp:1516 --suppress=unusedFunction:lib/importproject.cpp:1540" ./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib -D__CPPCHECK__ -D__GNUC__ --enable=unusedFunction,information --exception-handling -rp=. --project=cmake.output.notest_nogui/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr $supprs env: DISABLE_VALUEFLOW: 1 diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 32c8a14d222..acd7842224f 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -210,11 +210,10 @@ ImportProject::Type ImportProject::import(const std::string &filename, Settings return ImportProject::Type::FAILURE; } -static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[]) +static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[], bool str = false) { std::string ret; bool escapedString = false; - bool str = false; bool escape = false; for (; *pos < command.size() && (str || !std::strchr(until, command[*pos])); (*pos)++) { if (escape) @@ -266,6 +265,13 @@ void ImportProject::fsParseCommand(FileSettings& fs, const std::string& command, pos++; if (pos >= command.size()) break; + bool wholeArgQuoted = false; + if (command[pos] == '"') { + wholeArgQuoted = true; + pos++; + if (pos >= command.size()) + break; + } if (command[pos] != '/' && command[pos] != '-') continue; pos++; @@ -276,7 +282,9 @@ void ImportProject::fsParseCommand(FileSettings& fs, const std::string& command, while (pos < command.size() && command[pos] == ' ') ++pos; } - std::string fval = readUntil(command, &pos, " ="); + std::string fval = readUntil(command, &pos, " =", wholeArgQuoted); + if (wholeArgQuoted && fval.back() == '\"') + fval.resize(fval.size() - 1); if (F=='D') { std::string defval = readUntil(command, &pos, " "); defs += fval; diff --git a/test/testimportproject.cpp b/test/testimportproject.cpp index ee6a031711a..5e6db0163c8 100644 --- a/test/testimportproject.cpp +++ b/test/testimportproject.cpp @@ -66,6 +66,7 @@ class TestImportProject : public TestFixture { TEST_CASE(importCompileCommands12); // #13040: "directory" is parent directory, relative include paths TEST_CASE(importCompileCommands13); // #13333: duplicate file entries TEST_CASE(importCompileCommands14); // #14156 + TEST_CASE(importCompileCommands15); // #14306 TEST_CASE(importCompileCommandsArgumentsSection); // Handle arguments section TEST_CASE(importCompileCommandsNoCommandSection); // gracefully handles malformed json TEST_CASE(importCompileCommandsDirectoryMissing); // 'directory' field missing @@ -389,6 +390,26 @@ class TestImportProject : public TestFixture { ASSERT_EQUALS("TFS_LINUX_MODULE_NAME=\"tfs_linux\"", fs.defines); } + void importCompileCommands15() const { // #14306 + REDIRECT; + constexpr char json[] = + R"([ + { + "directory": "C:\\Users\\abcd\\efg\\hijk", + "command": "gcc \"-Ipath\\123\" \"-c\" test.c", + "file": "test.c", + "output": "test.obj" + } + ])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(1, importer.fileSettings.size()); + const FileSettings &fs = importer.fileSettings.front(); + ASSERT_EQUALS(1, fs.includePaths.size()); + ASSERT_EQUALS("C:/Users/abcd/efg/hijk/path/123/", fs.includePaths.front()); + } + void importCompileCommandsArgumentsSection() const { REDIRECT; constexpr char json[] = "[ { \"directory\": \"/tmp/\","