Refactor FindEntry and use best-fit .ccls

Fixes an issue with hierarchical .ccls found by Riatre in #384
This commit is contained in:
Fangrui Song 2019-04-29 08:44:52 -07:00
parent 16bd93b24b
commit 6624108fd9

View File

@ -240,8 +240,6 @@ std::vector<const char *> GetFallback(const std::string path) {
std::vector<const char *> argv{"clang"}; std::vector<const char *> argv{"clang"};
if (sys::path::extension(path) == ".h") if (sys::path::extension(path) == ".h")
argv.push_back("-xobjective-c++-header"); argv.push_back("-xobjective-c++-header");
for (const std::string &arg : g_config->clang.extraArgs)
argv.push_back(Intern(arg));
argv.push_back(Intern(path)); argv.push_back(Intern(path));
return argv; return argv;
} }
@ -442,13 +440,6 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) {
sys::path::append(Path, root, ".ccls"); sys::path::append(Path, root, ".ccls");
if (sys::fs::exists(Path)) if (sys::fs::exists(Path))
LoadDirectoryListing(proc, root, Seen); LoadDirectoryListing(proc, root, Seen);
std::vector<const char *> extra_args;
for (const std::string &arg : g_config->clang.extraArgs)
extra_args.push_back(Intern(arg));
for (auto &e : folder.entries) {
e.args.insert(e.args.end(), extra_args.begin(), extra_args.end());
e.args.push_back(Intern("-working-directory=" + e.directory));
}
} }
void Project::Load(const std::string &root) { void Project::Load(const std::string &root) {
@ -470,94 +461,106 @@ void Project::Load(const std::string &root) {
Project::Entry Project::FindEntry(const std::string &path, bool can_redirect, Project::Entry Project::FindEntry(const std::string &path, bool can_redirect,
bool must_exist) { bool must_exist) {
Project::Folder *best_folder = nullptr; std::string best_dot_ccls_root;
Project::Folder *best_dot_ccls_folder = nullptr;
std::string best_dot_ccls_dir;
const std::vector<const char *> *best_dot_ccls_args = nullptr;
bool match = false, exact_match = false;
const Entry *best = nullptr; const Entry *best = nullptr;
std::lock_guard lock(mtx); Project::Folder *best_compdb_folder = nullptr;
for (auto &[root, folder] : root2folder) {
// The entry may have different filename but it doesn't matter when building
// CompilerInvocation. The main filename is specified separately.
auto it = folder.path2entry_index.find(path);
if (it != folder.path2entry_index.end()) {
Project::Entry &entry = folder.entries[it->second];
if (can_redirect || entry.filename == path)
return entry;
if (entry.compdb_size) {
best_folder = &folder;
best = &entry;
}
break;
}
}
bool exists = false;
std::string dir;
const std::vector<const char *> *extra = nullptr;
Project::Entry ret; Project::Entry ret;
std::lock_guard lock(mtx);
for (auto &[root, folder] : root2folder) for (auto &[root, folder] : root2folder)
if (StringRef(path).startswith(root)) if (StringRef(path).startswith(root)) {
for (auto &[dir1, args] : folder.dot_ccls) // Find the best-fit .ccls
if (StringRef(path).startswith(dir1)) { for (auto &[dir, args] : folder.dot_ccls)
dir = dir1; if (StringRef(path).startswith(dir) &&
extra = &args; dir.length() > best_dot_ccls_dir.length()) {
if (AppendToCDB(args)) best_dot_ccls_root = root;
goto out; best_dot_ccls_folder = &folder;
exists = true; best_dot_ccls_dir = dir;
best_dot_ccls_args = &args;
ret.root = ret.directory = root;
ret.filename = path;
if (args.empty()) {
ret.args = GetFallback(path);
} else {
ret.args = args;
ret.args.push_back(Intern(path));
}
ProjectProcessor(folder).Process(ret);
for (const std::string &arg : g_config->clang.extraArgs)
ret.args.push_back(Intern(arg));
ret.args.push_back(Intern("-working-directory=" + ret.directory));
return ret;
} }
out:
if (must_exist && !exists)
return ret;
if (!best) { if (!match) {
int best_score = INT_MIN; auto it = folder.path2entry_index.find(path);
for (auto &[root, folder] : root2folder) { if (it != folder.path2entry_index.end()) {
if (dir.size() && !StringRef(path).startswith(dir)) Project::Entry &entry = folder.entries[it->second];
continue; exact_match = entry.filename == path;
for (const Entry &e : folder.entries) if ((match = exact_match || can_redirect) || entry.compdb_size) {
if (e.compdb_size) { // best->compdb_size is >0 for a compdb entry, 0 for a .ccls entry.
int score = ComputeGuessScore(path, e.filename); best_compdb_folder = &folder;
if (score > best_score) { best = &entry;
best_score = score;
best = &e;
best_folder = &folder;
} }
} }
}
} }
}
ret.is_inferred = true; bool append;
ret.filename = path; if (best_dot_ccls_args && !(append = AppendToCDB(*best_dot_ccls_args)) &&
if (!best) { !exact_match) {
if (ret.root.empty()) // If the first line is not %compile_commands.json, override the compdb
ret.root = g_config->fallbackFolder; // match if it is not an exact match.
ret.directory = ret.root; ret.root = ret.directory = best_dot_ccls_root;
ret.args = GetFallback(path); ret.filename = path;
if (best_dot_ccls_args->empty()) {
ret.args = GetFallback(path);
} else {
ret.args = *best_dot_ccls_args;
ret.args.push_back(Intern(path));
}
} else { } else {
ret.root = best->root; // If the first line is %compile_commands.json, find the matching compdb
ret.directory = best->directory; // entry and append .ccls args.
ret.args = best->args; if (must_exist && !match && !(best_dot_ccls_args && !append))
ret.args.resize(best->compdb_size); return ret;
if (extra && extra->size()) if (!best) {
ret.args.insert(ret.args.end(), extra->begin() + 1, extra->end()); // Infer args from a similar path.
ProjectProcessor(*best_folder).Process(ret); int best_score = INT_MIN;
for (const std::string &arg : g_config->clang.extraArgs) for (auto &[root, folder] : root2folder)
ret.args.push_back(Intern(arg)); if (StringRef(path).startswith(root))
ret.args.push_back(Intern("-working-directory=" + ret.directory)); for (const Entry &e : folder.entries)
if (e.compdb_size) {
int score = ComputeGuessScore(path, e.filename);
if (score > best_score) {
best_score = score;
best_compdb_folder = &folder;
best = &e;
}
}
ret.is_inferred = true;
}
if (!best) {
ret.root = ret.directory = g_config->fallbackFolder;
ret.args = GetFallback(path);
} else {
// The entry may have different filename but it doesn't matter when
// building CompilerInvocation. The main filename is specified
// separately.
ret.root = best->root;
ret.directory = best->directory;
ret.args = best->args;
if (best->compdb_size) // delete trailing .ccls options if exist
ret.args.resize(best->compdb_size);
else
best_dot_ccls_args = nullptr;
}
ret.filename = path;
} }
if (best_dot_ccls_args && append && best_dot_ccls_args->size())
ret.args.insert(ret.args.end(), best_dot_ccls_args->begin() + 1,
best_dot_ccls_args->end());
if (best_compdb_folder)
ProjectProcessor(*best_compdb_folder).Process(ret);
else if (best_dot_ccls_folder)
ProjectProcessor(*best_dot_ccls_folder).Process(ret);
for (const std::string &arg : g_config->clang.extraArgs)
ret.args.push_back(Intern(arg));
ret.args.push_back(Intern("-working-directory=" + ret.directory));
return ret; return ret;
} }
@ -565,6 +568,9 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
auto &gi = g_config->index; auto &gi = g_config->index;
GroupMatch match(gi.whitelist, gi.blacklist), GroupMatch match(gi.whitelist, gi.blacklist),
match_i(gi.initialWhitelist, gi.initialBlacklist); match_i(gi.initialWhitelist, gi.initialBlacklist);
std::vector<const char *> args, extra_args;
for (const std::string &arg : g_config->clang.extraArgs)
extra_args.push_back(Intern(arg));
{ {
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
for (auto &[root, folder] : root2folder) { for (auto &[root, folder] : root2folder) {
@ -574,7 +580,10 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
if (match.Matches(entry.filename, &reason) && if (match.Matches(entry.filename, &reason) &&
match_i.Matches(entry.filename, &reason)) { match_i.Matches(entry.filename, &reason)) {
bool interactive = wfiles->GetFile(entry.filename) != nullptr; bool interactive = wfiles->GetFile(entry.filename) != nullptr;
pipeline::Index(entry.filename, entry.args, args = entry.args;
args.insert(args.end(), extra_args.begin(), extra_args.end());
args.push_back(Intern("-working-directory=" + entry.directory));
pipeline::Index(entry.filename, args,
interactive ? IndexMode::Normal interactive ? IndexMode::Normal
: IndexMode::Background, : IndexMode::Background,
false, id); false, id);
@ -597,15 +606,20 @@ void Project::IndexRelated(const std::string &path) {
auto &gi = g_config->index; auto &gi = g_config->index;
GroupMatch match(gi.whitelist, gi.blacklist); GroupMatch match(gi.whitelist, gi.blacklist);
std::string stem = sys::path::stem(path); std::string stem = sys::path::stem(path);
std::vector<const char *> args, extra_args;
for (const std::string &arg : g_config->clang.extraArgs)
extra_args.push_back(Intern(arg));
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
for (auto &[root, folder] : root2folder) for (auto &[root, folder] : root2folder)
if (StringRef(path).startswith(root)) { if (StringRef(path).startswith(root)) {
for (const Project::Entry &entry : folder.entries) { for (const Project::Entry &entry : folder.entries) {
std::string reason; std::string reason;
args = entry.args;
args.insert(args.end(), extra_args.begin(), extra_args.end());
args.push_back(Intern("-working-directory=" + entry.directory));
if (sys::path::stem(entry.filename) == stem && entry.filename != path && if (sys::path::stem(entry.filename) == stem && entry.filename != path &&
match.Matches(entry.filename, &reason)) match.Matches(entry.filename, &reason))
pipeline::Index(entry.filename, entry.args, IndexMode::Background, pipeline::Index(entry.filename, args, IndexMode::Background, true);
true);
} }
break; break;
} }