From 7efd84ecaf29eff2cf331811f00f786090740dcb Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 6 Sep 2020 15:35:14 +0200 Subject: [PATCH] Fix false skipping of folder-seperator (#2749) --- lib/importproject.cpp | 212 ++++++++++++++++++++----------------- lib/importproject.h | 6 ++ test/testimportproject.cpp | 42 +++++++- 3 files changed, 160 insertions(+), 100 deletions(-) diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 79164a20d..6fa298e87 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -162,7 +162,7 @@ void ImportProject::FileSettings::setIncludePaths(const std::string &basepath, c continue; if (it.compare(0,2,"%(")==0) continue; - std::string s(Path::fromNativeSeparators(it)); + std::string s(Path::removeQuotationMarks(Path::fromNativeSeparators(it))); if (s[0] == '/' || (s.size() > 1U && s.compare(1,2,":/") == 0)) { if (!endsWith(s,'/')) s += '/'; @@ -217,13 +217,13 @@ ImportProject::Type ImportProject::import(const std::string &filename, Settings return ImportProject::Type::UNKNOWN; } -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 skipBackSlash = true) { std::string ret; bool str = false; while (*pos < command.size() && (str || !std::strchr(until, command[*pos]))) { - if (command[*pos] == '\\') - ++*pos; + if (skipBackSlash && command[*pos] == '\\') + ++* pos; if (*pos < command.size()) ret += command[(*pos)++]; if (endsWith(ret, '\"')) @@ -232,112 +232,126 @@ static std::string readUntil(const std::string &command, std::string::size_type return ret; } +static void skip_whitespaces(const std::string& command, std::string::size_type *pos) +{ + while (*pos < command.size() && command[*pos] == ' ') + (*pos)++; +} + + +static std::string parseTillNextCommandOpt(const std::string& singleCharOpts, const std::string& command, std::string::size_type *pos) +{ + std::string ret; + *pos = command.find_first_not_of("/-", *pos); + + if (*pos >= command.size()) + return ret; + + const char F = command[*pos]; + if (std::strchr(singleCharOpts.c_str(), F)) { + (*pos)++; + return std::string{F}; + } + + ret = readUntil(command, pos, " ="); + return ret; +} + +void ImportProject::FileSettings::parseCommandStd(const std::string& command, std::string::size_type *pos, std::string& defs) +{ + std::string def{}; + const std::string stdval = readUntil(command, pos, " "); + standard = stdval; + if (standard.compare(0, 3, "c++") || standard.compare(0, 5, "gnu++")) { + std::string stddef; + if (standard == "c++98" || standard == "gnu++98" || standard == "c++03" || standard == "gnu++03") { + stddef = "199711L"; + } else if (standard == "c++11" || standard == "gnu++11"|| standard == "c++0x" || standard == "gnu++0x") { + stddef = "201103L"; + } else if (standard == "c++14" || standard == "gnu++14" || standard == "c++1y" || standard == "gnu++1y") { + stddef = "201402L"; + } else if (standard == "c++17" || standard == "gnu++17" || standard == "c++1z" || standard == "gnu++1z") { + stddef = "201703L"; + } + + if (stddef.empty()) { + // TODO: log error + } else { + def += "__cplusplus="; + def += stddef; + def += ";"; + } + } + defs += def; +} + +void ImportProject::FileSettings::parseCommandDefine(const std::string& command, std::string::size_type *pos, std::string& defs) +{ + const bool skipBackSlash = false; + defs += readUntil(command, pos, " ="); + const std::string defval = readUntil(command, pos, " ", skipBackSlash); + if (!defval.empty()) + defs += defval; + defs += ';'; +} + +void ImportProject::FileSettings::parseCommandUndefine(const std::string& command, std::string::size_type *pos) +{ + const std::string fval = readUntil(command, pos, " "); + undefs.insert(fval); +} + +void ImportProject::FileSettings::parseCommandInclude(const std::string& command, std::string::size_type *pos) +{ + const bool skipBackSlash = false; + const std::string fval = readUntil(command, pos, " ", skipBackSlash); + if (std::find(includePaths.begin(), includePaths.end(), fval) == includePaths.end()) + includePaths.push_back(fval); +} + +void ImportProject::FileSettings::parseCommandSystemInclude(const std::string& command, std::string::size_type *pos) +{ + const bool skipBackSlash = false; + const std::string isystem = Path::removeQuotationMarks(readUntil(command, pos, " ", skipBackSlash)); + systemIncludePaths.push_back(isystem); +} + void ImportProject::FileSettings::parseCommand(const std::string &command) { + const std::string singleCharCommandOpts = "DUI"; std::string defs; - // Parse command.. std::string::size_type pos = 0; while (std::string::npos != (pos = command.find(' ',pos))) { - while (pos < command.size() && command[pos] == ' ') - pos++; + skip_whitespaces(command, &pos); if (pos >= command.size()) break; - if (command[pos] != '/' && command[pos] != '-') - continue; - pos++; + + const auto opt = parseTillNextCommandOpt(singleCharCommandOpts, command, &pos); + if (pos >= command.size()) break; - const char F = command[pos++]; - if (std::strchr("DUI", F)) { - while (pos < command.size() && command[pos] == ' ') - ++pos; - } - const std::string fval = readUntil(command, &pos, " ="); - if (F=='D') { - const std::string defval = readUntil(command, &pos, " "); - defs += fval; - if (!defval.empty()) - defs += defval; - defs += ';'; - } else if (F=='U') - undefs.insert(fval); - else if (F=='I') { - if (std::find(includePaths.begin(), includePaths.end(), fval) == includePaths.end()) - includePaths.push_back(fval); - } else if (F=='s' && fval.compare(0,2,"td") == 0) { - ++pos; - const std::string stdval = readUntil(command, &pos, " "); - standard = stdval; - if (standard.compare(0, 3, "c++") || standard.compare(0, 5, "gnu++")) { - std::string stddef; - if (standard == "c++98" || standard == "gnu++98" || standard == "c++03" || standard == "gnu++03") { - stddef = "199711L"; - } else if (standard == "c++11" || standard == "gnu++11"|| standard == "c++0x" || standard == "gnu++0x") { - stddef = "201103L"; - } else if (standard == "c++14" || standard == "gnu++14" || standard == "c++1y" || standard == "gnu++1y") { - stddef = "201402L"; - } else if (standard == "c++17" || standard == "gnu++17" || standard == "c++1z" || standard == "gnu++1z") { - stddef = "201703L"; - } - if (stddef.empty()) { - // TODO: log error - continue; - } - - defs += "__cplusplus="; - defs += stddef; - defs += ";"; - } else if (standard.compare(0, 1, "c") || standard.compare(0, 3, "gnu")) { - if (standard == "c90" || standard == "iso9899:1990" || standard == "gnu90" || standard == "iso9899:199409") { - // __STDC_VERSION__ is not set for C90 although the macro was added in the 1994 amendments - continue; - } - - std::string stddef; - - if (standard == "c99" || standard == "iso9899:1999" || standard == "gnu99") { - stddef = "199901L"; - } else if (standard == "c11" || standard == "iso9899:2011" || standard == "gnu11" || standard == "c1x" || standard == "gnu1x") { - stddef = "201112L"; - } else if (standard == "c17") { - stddef = "201710L"; - } - - if (stddef.empty()) { - // TODO: log error - continue; - } - - defs += "__STDC_VERSION__="; - defs += stddef; - defs += ";"; - } - } else if (F == 'i' && fval == "system") { - ++pos; - const std::string isystem = readUntil(command, &pos, " "); - systemIncludePaths.push_back(isystem); - } else if (F=='m') { - if (fval == "unicode") { - defs += "UNICODE"; - defs += ";"; - } - } else if (F=='f') { - if (fval == "pic") { - defs += "__pic__"; - defs += ";"; - } else if (fval == "PIC") { - defs += "__PIC__"; - defs += ";"; - } else if (fval == "pie") { - defs += "__pie__"; - defs += ";"; - } else if (fval == "PIE") { - defs += "__PIE__"; - defs += ";"; - } - } + if (opt=="D") + parseCommandDefine(command, &pos, defs); + else if (opt=="U") + parseCommandUndefine(command, &pos); + else if (opt=="I") + parseCommandInclude(command, &pos); + else if (opt=="isystem") + parseCommandSystemInclude(command, &pos); + else if (opt=="std") + parseCommandStd(command, &pos, defs); + else if (opt=="municode") + defs += "UNICODE;"; + else if (opt=="fpic") + defs += "__pic__;"; + else if (opt=="fPIC") + defs += "__PIC__;"; + else if (opt=="fpie") + defs += "__pie__;"; + else if (opt=="fPIE") + defs += "__PIE__;"; } setDefines(defs); } diff --git a/lib/importproject.h b/lib/importproject.h index 9694d6468..f3bf5258d 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -77,6 +77,12 @@ public: bool useMfc; void parseCommand(const std::string &command); + + void parseCommandStd(const std::string& command, std::string::size_type *pos, std::string& defs); + void parseCommandDefine(const std::string& command, std::string::size_type *pos, std::string& defs); + void parseCommandUndefine(const std::string& command, std::string::size_type *pos); + void parseCommandInclude(const std::string& command, std::string::size_type *pos); + void parseCommandSystemInclude(const std::string& command, std::string::size_type *pos); void setDefines(std::string defs); void setIncludePaths(const std::string &basepath, const std::list &in, std::map &variables); }; diff --git a/test/testimportproject.cpp b/test/testimportproject.cpp index d7162434c..3941b1f29 100644 --- a/test/testimportproject.cpp +++ b/test/testimportproject.cpp @@ -47,6 +47,8 @@ private: TEST_CASE(importCompileCommands2); // #8563 TEST_CASE(importCompileCommands3); // check with existing trailing / in directory TEST_CASE(importCompileCommands4); // only accept certain file types + TEST_CASE(importCompileCommands5); // Windows/CMake/Ninja generated comile_commands.json + TEST_CASE(importCompileCommands6); // Windows/CMake/Ninja generated comile_commands.json with spaces TEST_CASE(importCompileCommandsArgumentsSection); // Handle arguments section TEST_CASE(importCompileCommandsNoCommandSection); // gracefully handles malformed json TEST_CASE(importCppcheckGuiProject); @@ -100,7 +102,7 @@ private: void importCompileCommands1() const { const char json[] = "[ { \"directory\": \"/tmp\"," - "\"command\": \"gcc -I/tmp -DFILESDIR=\\\\\\\"/usr/local/share/Cppcheck\\\\\\\" -DTEST1 -DTEST2=2 -o /tmp/src.o -c /tmp/src.c\"," + "\"command\": \"gcc -I/tmp -DFILESDIR=\\\"/usr/local/share/Cppcheck\\\" -DTEST1 -DTEST2=2 -o /tmp/src.o -c /tmp/src.c\"," "\"file\": \"/tmp/src.c\" } ]"; std::istringstream istr(json); TestImporter importer; @@ -141,6 +143,44 @@ private: ASSERT_EQUALS(0, importer.fileSettings.size()); } + void importCompileCommands5() const { + const char json[] = + "[{" + "\"directory\": \"C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug\"," + "\"command\": \"C:\\\\PROGRA~2\\\\MICROS~1\\\\2019\\\\COMMUN~1\\\\VC\\\\Tools\\\\MSVC\\\\1427~1.291\\\\bin\\\\HostX64\\\\x64\\\\cl.exe /nologo /TP -IC:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\mylib\\\\src /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomylib\\\\CMakeFiles\\\\mylib.dir\\\\src\\\\foobar\\\\mylib.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\mylib\\\\src\\\\foobar\\\\mylib.cpp\"," + "\"file\": \"C:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\mylib\\\\src\\\\foobar\\\\mylib.cpp\"" + "}," + "{" + "\"directory\": \"C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug\"," + "\"command\": \"C:\\\\PROGRA~2\\\\MICROS~1\\\\2019\\\\COMMUN~1\\\\VC\\\\Tools\\\\MSVC\\\\1427~1.291\\\\bin\\\\HostX64\\\\x64\\\\cl.exe /nologo /TP -IC:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\myapp\\\\src -Imyapp -IC:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\mylib\\\\src /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomyapp\\\\CMakeFiles\\\\myapp.dir\\\\src\\\\main.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\myapp\\\\src\\\\main.cpp\"," + "\"file\": \"C:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\myapp\\\\src\\\\main.cpp\"" + "}]"; + std::istringstream istr(json); + TestImporter importer; + importer.importCompileCommands(istr); + ASSERT_EQUALS(2, importer.fileSettings.size()); + ASSERT_EQUALS("C:/Users/dan/git/test-cppcheck/mylib/src/", importer.fileSettings.begin()->includePaths.front()); + } + + void importCompileCommands6() const { + const char json[] = + "[{" + "\"directory\": \"C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug\"," + "\"command\": \"C:\\\\PROGRA~2\\\\MICROS~1\\\\2019\\\\COMMUN~1\\\\VC\\\\Tools\\\\MSVC\\\\1427~1.291\\\\bin\\\\HostX64\\\\x64\\\\cl.exe /nologo /TP -IC:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\mylib\\\\src -I\\\"C:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\mylib\\\\second src\\\" /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomylib\\\\CMakeFiles\\\\mylib.dir\\\\src\\\\foobar\\\\mylib.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\mylib\\\\src\\\\foobar\\\\mylib.cpp\"," + "\"file\": \"C:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\mylib\\\\src\\\\foobar\\\\mylib.cpp\"" + "}," + "{" + "\"directory\": \"C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug\"," + "\"command\": \"C:\\\\PROGRA~2\\\\MICROS~1\\\\2019\\\\COMMUN~1\\\\VC\\\\Tools\\\\MSVC\\\\1427~1.291\\\\bin\\\\HostX64\\\\x64\\\\cl.exe /nologo /TP -IC:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\myapp\\\\src -Imyapp -IC:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\mylib\\\\src /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomyapp\\\\CMakeFiles\\\\myapp.dir\\\\src\\\\main.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\myapp\\\\src\\\\main.cpp\"," + "\"file\": \"C:\\\\Users\\\\dan\\\\git\\\\test-cppcheck\\\\myapp\\\\src\\\\main.cpp\"" + "}]"; + std::istringstream istr(json); + TestImporter importer; + importer.importCompileCommands(istr); + ASSERT_EQUALS(2, importer.fileSettings.size()); + ASSERT_EQUALS("C:/Users/dan/git/test-cppcheck/mylib/second src/", importer.fileSettings.begin()->includePaths.front()); + } + void importCompileCommandsArgumentsSection() const { const char json[] = "[ { \"directory\": \"/tmp/\"," "\"arguments\": [\"gcc\", \"-c\", \"src.c\"],"