Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix #14433 (Import project: refactor code to read compile_commands.json)#8030
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
b757d8a167706ee1902c83044e7be9227a1f33fe3a55ac8b19cdf01b33c4119File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -42,6 +42,148 @@ | ||
| #include "json.h" | ||
| std::string ImportProject::collectArgs(const std::string &cmd, std::vector<std::string> &args) | ||
| { | ||
| args.clear(); | ||
| std::string::size_type pos = 0; | ||
| const std::string::size_type end = cmd.size(); | ||
| std::string arg; | ||
| bool inDoubleQuotes = false; | ||
| bool inSingleQuotes = false; | ||
| while (pos < end) { | ||
| char c = cmd[pos++]; | ||
| if (c == ' ') { | ||
| if (inDoubleQuotes || inSingleQuotes) { | ||
| arg.push_back(c); | ||
| continue; | ||
| } | ||
| if (!arg.empty()) | ||
| args.push_back(arg); | ||
| arg.clear(); | ||
| pos = cmd.find_first_not_of(' ', pos); | ||
| continue; | ||
| } | ||
| if (c == '\"' && !inSingleQuotes) { | ||
| inDoubleQuotes = !inDoubleQuotes; | ||
| continue; | ||
| } | ||
| if (c == '\'' && !inDoubleQuotes) { | ||
| inSingleQuotes = !inSingleQuotes; | ||
| continue; | ||
| } | ||
| if (c == '\\' && !inSingleQuotes) { | ||
| if (pos == end) { | ||
| arg.push_back('\\'); | ||
| break; | ||
| } | ||
| c = cmd[pos++]; | ||
| if (!std::strchr("\\\"\' ", c)) | ||
| arg.push_back('\\'); | ||
| arg.push_back(c); | ||
| continue; | ||
danmar marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| arg.push_back(c); | ||
| } | ||
| if (inSingleQuotes || inDoubleQuotes) | ||
| return "Missing closing quote in command string"; | ||
| if (!arg.empty()) | ||
| args.push_back(arg); | ||
| return ""; | ||
| } | ||
| void ImportProject::parseArgs(FileSettings &fs, const std::vector<std::string> &args) | ||
| { | ||
| const auto getOptArg = [&args](std::initializer_list<std::string> optNames, | ||
danmar marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| std::size_t &i) { | ||
| const auto &arg = args[i]; | ||
| const auto *const it = std::find_if(optNames.begin(), | ||
| optNames.end(), | ||
| [&arg] (const std::string &optName) { | ||
| return startsWith(arg, optName); | ||
| }); | ||
| if (it == optNames.end()) | ||
| return std::string(); | ||
| const std::size_t optLen = it->size(); | ||
| if (arg.size() == optLen) | ||
| return ++i >= args.size() ? std::string() : args[i]; | ||
| return arg.substr(optLen); | ||
| }; | ||
| std::string defs; | ||
| for (std::size_t i = 0; i < args.size(); i++) { | ||
| std::string optArg; | ||
| if (!(optArg = getOptArg({ "-I", "/I" }, i)).empty()) { | ||
| if (std::none_of(fs.includePaths.cbegin(), fs.includePaths.cend(), | ||
| [&](const std::string &path) { | ||
| return path == optArg; | ||
| })) | ||
| fs.includePaths.push_back(std::move(optArg)); | ||
| continue; | ||
| } | ||
| if (!(optArg = getOptArg({ "-isystem" }, i)).empty()) { | ||
| fs.systemIncludePaths.push_back(std::move(optArg)); | ||
| continue; | ||
| } | ||
| if (!(optArg = getOptArg({ "-D", "/D" }, i)).empty()) { | ||
| defs += optArg + ";"; | ||
| continue; | ||
| } | ||
| if (!(optArg = getOptArg({ "-U", "/U" }, i)).empty()) { | ||
| fs.undefs.insert(std::move(optArg)); | ||
| continue; | ||
| } | ||
| if (!(optArg = getOptArg({ "-std=", "/std:" }, i)).empty()) { | ||
| fs.standard = std::move(optArg); | ||
| continue; | ||
| } | ||
| if (!(optArg = getOptArg({ "-f" }, i)).empty()) { | ||
| if (optArg == "pic") | ||
| defs += "__pic__;"; | ||
| else if (optArg == "PIC") | ||
| defs += "__PIC__;"; | ||
| else if (optArg == "pie") | ||
| defs += "__pie__;"; | ||
| else if (optArg == "PIE") | ||
| defs += "__PIE__;"; | ||
| continue; | ||
| } | ||
| if (!(optArg = getOptArg({ "-m" }, i)).empty()) { | ||
| if (optArg == "unicode") | ||
| defs += "UNICODE;"; | ||
| continue; | ||
| } | ||
| } | ||
| fsSetDefines(fs, std::move(defs)); | ||
| } | ||
| void ImportProject::ignorePaths(const std::vector<std::string> &ipaths, bool debug) | ||
| { | ||
| PathMatch matcher(ipaths, Path::getCurrentPath()); | ||
| @@ -210,134 +352,6 @@ 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[], bool str = false) | ||
| { | ||
| std::string ret; | ||
| bool escapedString = false; | ||
| bool escape = false; | ||
| for (; *pos < command.size() && (str || !std::strchr(until, command[*pos])); (*pos)++) { | ||
| if (escape) | ||
| escape = false; | ||
| else if (command[*pos] == '\\') { | ||
| if (str) | ||
| escape = true; | ||
| else if (command[*pos + 1] == '"') { | ||
| if (escapedString) | ||
| return ret + "\\\""; | ||
| escapedString = true; | ||
| ret += "\\\""; | ||
| (*pos)++; | ||
| continue; | ||
| } | ||
| } else if (command[*pos] == '\"') | ||
| str = !str; | ||
| ret += command[*pos]; | ||
| } | ||
| return ret; | ||
| } | ||
| static std::string unescape(const std::string &in) | ||
| { | ||
| std::string out; | ||
| bool escape = false; | ||
| for (const char c: in) { | ||
| if (escape) { | ||
| escape = false; | ||
| if (!std::strchr("\\\"\'",c)) | ||
| out += "\\"; | ||
| out += c; | ||
| } else if (c == '\\') | ||
| escape = true; | ||
| else | ||
| out += c; | ||
| } | ||
| return out; | ||
| } | ||
| void ImportProject::fsParseCommand(FileSettings& fs, const std::string& command, bool doUnescape) | ||
| { | ||
| 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++; | ||
| 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++; | ||
| if (pos >= command.size()) | ||
| break; | ||
| const char F = command[pos++]; | ||
| if (std::strchr("DUI", F)) { | ||
| while (pos < command.size() && command[pos] == ' ') | ||
| ++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; | ||
| if (doUnescape) { | ||
| if (defval.size() >= 3 && startsWith(defval,"=\"") && defval.back()=='\"') | ||
| defval = "=" + unescape(defval.substr(2, defval.size() - 3)); | ||
| else if (defval.size() >= 5 && startsWith(defval, "=\\\"") && endsWith(defval, "\\\"")) | ||
| defval = "=\"" + unescape(defval.substr(3, defval.size() - 5)) + "\""; | ||
| } | ||
| if (!defval.empty()) | ||
| defs += defval; | ||
| defs += ';'; | ||
| } else if (F=='U') | ||
| fs.undefs.insert(std::move(fval)); | ||
| else if (F=='I') { | ||
| std::string i = std::move(fval); | ||
| if (i.size() > 1 && i[0] == '\"' && i.back() == '\"') | ||
| i = unescape(i.substr(1, i.size() - 2)); | ||
| if (std::find(fs.includePaths.cbegin(), fs.includePaths.cend(), i) == fs.includePaths.cend()) | ||
| fs.includePaths.push_back(std::move(i)); | ||
| } else if (F=='s' && startsWith(fval,"td")) { | ||
| ++pos; | ||
| fs.standard = readUntil(command, &pos, " "); | ||
| } else if (F == 'i' && fval == "system") { | ||
| ++pos; | ||
| std::string isystem = readUntil(command, &pos, " "); | ||
| fs.systemIncludePaths.push_back(std::move(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 += ";"; | ||
| } | ||
| // TODO: support -fsigned-char and -funsigned-char? | ||
| // we can only set it globally but in this context it needs to be treated per file | ||
| } | ||
| } | ||
| fsSetDefines(fs, std::move(defs)); | ||
| } | ||
| bool ImportProject::importCompileCommands(std::istream &istr) | ||
| { | ||
| picojson::value compileCommands; | ||
| @@ -371,31 +385,31 @@ bool ImportProject::importCompileCommands(std::istream &istr) | ||
| const std::string directory = std::move(dirpath); | ||
| bool doUnescape = false; | ||
| std::string command; | ||
| std::vector<std::string> arguments; | ||
| if (obj.count("arguments")) { | ||
| doUnescape = false; | ||
| if (obj["arguments"].is<picojson::array>()) { | ||
| for (const picojson::value& arg : obj["arguments"].get<picojson::array>()) { | ||
| if (arg.is<std::string>()) { | ||
| std::string str = arg.get<std::string>(); | ||
| if (str.find(' ') != std::string::npos) | ||
| str = "\"" + str + "\""; | ||
| command += str + " "; | ||
| } | ||
| if (arg.is<std::string>()) | ||
| arguments.push_back(arg.get<std::string>()); | ||
| } | ||
| } else { | ||
| errors.emplace_back("'arguments' field in compilation database entry is not a JSON array"); | ||
| return false; | ||
| } | ||
| } else if (obj.count("command")) { | ||
| doUnescape = true; | ||
| std::string command; | ||
| if (obj["command"].is<std::string>()) { | ||
| command = obj["command"].get<std::string>(); | ||
| } else { | ||
| errors.emplace_back("'command' field in compilation database entry is not a string"); | ||
| return false; | ||
| } | ||
| std::string error = collectArgs(command, arguments); | ||
| if (!error.empty()) { | ||
| errors.emplace_back(error); | ||
| return false; | ||
| } | ||
| } else { | ||
| errors.emplace_back("no 'arguments' or 'command' field found in compilation database entry"); | ||
| return false; | ||
| @@ -425,7 +439,7 @@ bool ImportProject::importCompileCommands(std::istream &istr) | ||
| else | ||
| path = Path::simplifyPath(directory + file); | ||
| FileSettings fs{path, Standards::Language::None, 0}; // file will be identified later on | ||
| fsParseCommand(fs, command, doUnescape); // read settings; -D, -I, -U, -std, -m*, -f* | ||
| parseArgs(fs, arguments); | ||
| std::map<std::string, std::string, cppcheck::stricmp> variables; | ||
| fsSetIncludePaths(fs, directory, fs.includePaths, variables); | ||
| // Assign a unique index to each file path. If the file path already exists in the map, | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.