Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/selfcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ jobs:

- name: Self check (unusedFunction / no test / no gui)
run: |
supprs="--suppress=unusedFunction:lib/errorlogger.h:196 --suppress=unusedFunction:lib/importproject.cpp:1516 --suppress=unusedFunction:lib/importproject.cpp:1540"
supprs="--suppress=unusedFunction:lib/errorlogger.h:196 --suppress=unusedFunction:lib/importproject.cpp:1530 --suppress=unusedFunction:lib/importproject.cpp:1554"
./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
Expand Down
292 changes: 153 additions & 139 deletions lib/importproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

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,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest std::vector<std::string> argument:

Suggested change
const auto getOptArg = [&args](std::initializer_list<std::string> optNames,
const auto getOptArg = [&args](std::vector<std::string> optNames,

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From cppreference:

An object of type std::initializer_list is a lightweight proxy object that provides access to an array of objects of type const T (that may be allocated in read-only memory).

Since we are not modifying these lists in any way I think it's unnecessary to use a vector.

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());
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion lib/importproject.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class CPPCHECKLIB WARN_UNUSED ImportProject {
CPPCHECK_GUI
};

static void fsParseCommand(FileSettings& fs, const std::string& command, bool doUnescape);
static void fsSetDefines(FileSettings& fs, std::string defs);
static void fsSetIncludePaths(FileSettings& fs, const std::string &basepath, const std::list<std::string> &in, std::map<std::string, std::string, cppcheck::stricmp> &variables);

Expand Down Expand Up @@ -103,6 +102,8 @@ class CPPCHECKLIB WARN_UNUSED ImportProject {
protected:
bool importCompileCommands(std::istream &istr);
bool importCppcheckGuiProject(std::istream &istr, Settings &settings, Suppressions &supprs);
static std::string collectArgs(const std::string &cmd, std::vector<std::string> &args);
static void parseArgs(FileSettings &fs, const std::vector<std::string> &args);

private:
struct SharedItemsProject {
Expand Down
Loading
Loading