This commit is contained in:
Fangrui Song 2018-03-31 13:59:27 -07:00
parent a58a38c32d
commit c96631d1ee
23 changed files with 74 additions and 379 deletions

View File

@ -179,6 +179,7 @@ target_sources(ccls PRIVATE
src/clang_translation_unit.cc src/clang_translation_unit.cc
src/clang_utils.cc src/clang_utils.cc
src/command_line.cc src/command_line.cc
src/config.cc
src/diagnostics_engine.cc src/diagnostics_engine.cc
src/file_consumer.cc src/file_consumer.cc
src/file_contents.cc src/file_contents.cc
@ -209,7 +210,6 @@ target_sources(ccls PRIVATE
src/timestamp_manager.cc src/timestamp_manager.cc
src/type_printer.cc src/type_printer.cc
src/utils.cc src/utils.cc
src/work_thread.cc
src/working_files.cc) src/working_files.cc)
target_sources(ccls PRIVATE target_sources(ccls PRIVATE
@ -217,7 +217,6 @@ target_sources(ccls PRIVATE
src/messages/ccls_call_hierarchy.cc src/messages/ccls_call_hierarchy.cc
src/messages/ccls_callers.cc src/messages/ccls_callers.cc
src/messages/ccls_derived.cc src/messages/ccls_derived.cc
src/messages/ccls_did_view.cc
src/messages/ccls_file_info.cc src/messages/ccls_file_info.cc
src/messages/ccls_freshen_index.cc src/messages/ccls_freshen_index.cc
src/messages/ccls_index_file.cc src/messages/ccls_index_file.cc
@ -225,7 +224,6 @@ target_sources(ccls PRIVATE
src/messages/ccls_member_hierarchy.cc src/messages/ccls_member_hierarchy.cc
src/messages/ccls_random.cc src/messages/ccls_random.cc
src/messages/ccls_vars.cc src/messages/ccls_vars.cc
src/messages/ccls_wait.cc
src/messages/exit.cc src/messages/exit.cc
src/messages/initialize.cc src/messages/initialize.cc
src/messages/shutdown.cc src/messages/shutdown.cc

View File

@ -9,10 +9,10 @@
#include <loguru.hpp> #include <loguru.hpp>
#include <limits.h>
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <climits>
#include <iostream> #include <iostream>
// TODO: See if we can use clang_indexLoc_getFileLocation to get a type ref on // TODO: See if we can use clang_indexLoc_getFileLocation to get a type ref on
@ -861,7 +861,7 @@ CXIdxClientFile OnIndexIncludedFile(CXClientData client_data,
IndexInclude include; IndexInclude include;
include.line = line; include.line = line;
include.resolved_path = FileName(file->file); include.resolved_path = FileName(file->file);
if (!include.resolved_path.empty()) if (include.resolved_path.size())
db->includes.push_back(include); db->includes.push_back(include);
return nullptr; return nullptr;

View File

@ -1,5 +1,6 @@
#include "clang_utils.h" #include "clang_utils.h"
#include "filesystem.hh"
#include "platform.h" #include "platform.h"
namespace { namespace {
@ -106,8 +107,11 @@ std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
std::string FileName(CXFile file) { std::string FileName(CXFile file) {
CXString cx_name = clang_getFileName(file); CXString cx_name = clang_getFileName(file);
std::string name = ToString(cx_name); std::string ret = NormalizePath(ToString(cx_name));
return NormalizePath(name); // Resolve /usr/include/c++/7.3.0 symlink.
if (!StartsWith(ret, g_config.projectRoot))
ret = fs::canonical(ret);
return ret;
} }
std::string ToString(CXString cx_string) { std::string ToString(CXString cx_string) {

View File

@ -22,7 +22,6 @@
#include "test.h" #include "test.h"
#include "timer.h" #include "timer.h"
#include "timestamp_manager.h" #include "timestamp_manager.h"
#include "work_thread.h"
#include "working_files.h" #include "working_files.h"
#include <doctest/doctest.h> #include <doctest/doctest.h>
@ -35,7 +34,6 @@
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <string> #include <string>
#include <thread>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@ -255,7 +253,7 @@ void LaunchStdinLoop(Config* config,
// clients. // clients.
std::cin.tie(nullptr); std::cin.tie(nullptr);
WorkThread::StartThread("stdin", [request_times]() { StartThread("stdin", [request_times]() {
auto* queue = QueueManager::instance(); auto* queue = QueueManager::instance();
while (true) { while (true) {
std::unique_ptr<InMessage> message; std::unique_ptr<InMessage> message;
@ -295,7 +293,7 @@ void LaunchStdinLoop(Config* config,
void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times, void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
MultiQueueWaiter* waiter) { MultiQueueWaiter* waiter) {
WorkThread::StartThread("stdout", [=]() { StartThread("stdout", [=]() {
auto* queue = QueueManager::instance(); auto* queue = QueueManager::instance();
while (true) { while (true) {

3
src/config.cc Normal file
View File

@ -0,0 +1,3 @@
#include "config.h"
Config g_config;

View File

@ -281,5 +281,4 @@ MAKE_REFLECT_STRUCT(Config,
dumpAST); dumpAST);
// Expected client version. We show an error if this doesn't match. extern Config g_config;
constexpr const int kExpectedClientVersion = 3;

View File

@ -5,9 +5,6 @@
#include "project.h" #include "project.h"
#include "standard_includes.h" #include "standard_includes.h"
#include "timer.h" #include "timer.h"
#include "work_thread.h"
#include <thread>
namespace { namespace {
@ -116,7 +113,7 @@ void IncludeComplete::Rescan() {
config_->completion.includeBlacklist); config_->completion.includeBlacklist);
is_scanning = true; is_scanning = true;
WorkThread::StartThread("scan_includes", [this]() { StartThread("scan_includes", [this]() {
Timer timer; Timer timer;
InsertStlIncludes(); InsertStlIncludes();

View File

@ -1,40 +0,0 @@
#include "clang_complete.h"
#include "message_handler.h"
#include "working_files.h"
namespace {
MethodType kMethodType = "$ccls/textDocumentDidView";
struct In_CclsTextDocumentDidView : public NotificationInMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
lsDocumentUri textDocumentUri;
};
Params params;
};
MAKE_REFLECT_STRUCT(In_CclsTextDocumentDidView::Params, textDocumentUri);
MAKE_REFLECT_STRUCT(In_CclsTextDocumentDidView, params);
REGISTER_IN_MESSAGE(In_CclsTextDocumentDidView);
struct Handler_CclsDidView
: BaseMessageHandler<In_CclsTextDocumentDidView> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_CclsTextDocumentDidView* request) override {
std::string path = request->params.textDocumentUri.GetPath();
WorkingFile* working_file = working_files->GetFileByFilename(path);
if (!working_file)
return;
QueryFile* file = nullptr;
if (!FindFileOrFail(db, project, std::nullopt, path, &file))
return;
clang_complete->NotifyView(path);
if (file->def) {
EmitInactiveLines(working_file, file->def->inactive_regions);
EmitSemanticHighlighting(db, semantic_cache, working_file, file);
}
}
};
REGISTER_MESSAGE_HANDLER(Handler_CclsDidView);
} // namespace

View File

@ -1,47 +0,0 @@
#include "import_manager.h"
#include "import_pipeline.h"
#include "message_handler.h"
#include "queue_manager.h"
#include <loguru.hpp>
namespace {
MethodType kMethodType = "$ccls/wait";
struct In_CclsWait : public NotificationInMessage {
MethodType GetMethodType() const override { return kMethodType; }
};
MAKE_REFLECT_EMPTY_STRUCT(In_CclsWait);
REGISTER_IN_MESSAGE(In_CclsWait);
struct Handler_CclsWait : MessageHandler {
MethodType GetMethodType() const override { return kMethodType; }
void Run(std::unique_ptr<InMessage> request) override {
// TODO: use status message system here, then run querydb as normal? Maybe
// this cannot be a normal message, ie, it needs to be re-entrant.
LOG_S(INFO) << "Waiting for idle";
int idle_count = 0;
while (true) {
bool has_work = false;
has_work |= import_pipeline_status->num_active_threads != 0;
has_work |= QueueManager::instance()->HasWork();
has_work |=
QueryDb_ImportMain(config, db, import_manager, import_pipeline_status,
semantic_cache, working_files);
if (!has_work)
++idle_count;
else
idle_count = 0;
// There are race conditions between each of the three checks above,
// so we retry a bunch of times to try to avoid any.
if (idle_count > 10)
break;
}
LOG_S(INFO) << "Done waiting for idle";
}
};
REGISTER_MESSAGE_HANDLER(Handler_CclsWait);
} // namespace

View File

@ -8,7 +8,6 @@
#include "queue_manager.h" #include "queue_manager.h"
#include "serializers/json.h" #include "serializers/json.h"
#include "timer.h" #include "timer.h"
#include "work_thread.h"
#include "working_files.h" #include "working_files.h"
#include <loguru.hpp> #include <loguru.hpp>
@ -270,45 +269,13 @@ struct lsTextDocumentClientCapabilities {
// The client supports the following `CompletionItem` specific // The client supports the following `CompletionItem` specific
// capabilities. // capabilities.
std::optional<lsCompletionItem> completionItem; std::optional<lsCompletionItem> completionItem;
}; } completion;
// Capabilities specific to the `textDocument/completion`
std::optional<lsCompletion> completion;
struct lsGenericDynamicReg { struct lsGenericDynamicReg {
// Whether foo supports dynamic registration. // Whether foo supports dynamic registration.
std::optional<bool> dynamicRegistration; std::optional<bool> dynamicRegistration;
}; };
// Capabilities specific to the `textDocument/hover`
std::optional<lsGenericDynamicReg> hover;
// Capabilities specific to the `textDocument/signatureHelp`
std::optional<lsGenericDynamicReg> signatureHelp;
// Capabilities specific to the `textDocument/references`
std::optional<lsGenericDynamicReg> references;
// Capabilities specific to the `textDocument/documentHighlight`
std::optional<lsGenericDynamicReg> documentHighlight;
// Capabilities specific to the `textDocument/documentSymbol`
std::optional<lsGenericDynamicReg> documentSymbol;
// Capabilities specific to the `textDocument/formatting`
std::optional<lsGenericDynamicReg> formatting;
// Capabilities specific to the `textDocument/rangeFormatting`
std::optional<lsGenericDynamicReg> rangeFormatting;
// Capabilities specific to the `textDocument/onTypeFormatting`
std::optional<lsGenericDynamicReg> onTypeFormatting;
// Capabilities specific to the `textDocument/definition`
std::optional<lsGenericDynamicReg> definition;
// Capabilities specific to the `textDocument/codeAction`
std::optional<lsGenericDynamicReg> codeAction;
struct CodeLensRegistrationOptions : public lsGenericDynamicReg { struct CodeLensRegistrationOptions : public lsGenericDynamicReg {
// Code lens has a resolve provider as well. // Code lens has a resolve provider as well.
bool resolveProvider; bool resolveProvider;
@ -317,9 +284,6 @@ struct lsTextDocumentClientCapabilities {
// Capabilities specific to the `textDocument/codeLens` // Capabilities specific to the `textDocument/codeLens`
std::optional<CodeLensRegistrationOptions> codeLens; std::optional<CodeLensRegistrationOptions> codeLens;
// Capabilities specific to the `textDocument/documentLink`
std::optional<lsGenericDynamicReg> documentLink;
// Capabilities specific to the `textDocument/rename` // Capabilities specific to the `textDocument/rename`
std::optional<lsGenericDynamicReg> rename; std::optional<lsGenericDynamicReg> rename;
}; };
@ -344,18 +308,6 @@ MAKE_REFLECT_STRUCT(
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities, MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities,
synchronization, synchronization,
completion, completion,
hover,
signatureHelp,
references,
documentHighlight,
documentSymbol,
formatting,
rangeFormatting,
onTypeFormatting,
definition,
codeAction,
codeLens,
documentLink,
rename); rename);
struct lsClientCapabilities { struct lsClientCapabilities {
@ -512,29 +464,9 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
// Client capabilities // Client capabilities
{ {
const auto& cap = request->params.capabilities.textDocument; const auto& cap = request->params.capabilities.textDocument;
if (cap.completion && cap.completion->completionItem) if (cap.completion.completionItem)
config->client.snippetSupport = config->client.snippetSupport =
cap.completion->completionItem->snippetSupport.value_or(false); cap.completion.completionItem->snippetSupport.value_or(false);
}
// Check client version.
if (config->clientVersion.has_value() &&
*config->clientVersion != kExpectedClientVersion) {
Out_ShowLogMessage out;
out.display_type = Out_ShowLogMessage::DisplayType::Show;
out.params.type = lsMessageType::Error;
out.params.message =
"ccls client (v" + std::to_string(*config->clientVersion) +
") and server (v" + std::to_string(kExpectedClientVersion) +
") version mismatch. Please update ";
if (config->clientVersion > kExpectedClientVersion)
out.params.message += "the ccls binary.";
else
out.params.message +=
"your extension client (VSIX file). Make sure to uninstall "
"the ccls extension and restart vscode before "
"reinstalling.";
out.Write(std::cout);
} }
// Ensure there is a resource directory. // Ensure there is a resource directory.
@ -576,6 +508,7 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
MakeDirectoryRecursive(config->cacheDirectory + '@' + MakeDirectoryRecursive(config->cacheDirectory + '@' +
EscapeFileName(config->projectRoot)); EscapeFileName(config->projectRoot));
g_config = *config;
Timer time; Timer time;
diag_engine->Init(config); diag_engine->Init(config);
semantic_cache->Init(config); semantic_cache->Init(config);
@ -599,7 +532,7 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
} }
LOG_S(INFO) << "Starting " << config->index.threads << " indexers"; LOG_S(INFO) << "Starting " << config->index.threads << " indexers";
for (int i = 0; i < config->index.threads; ++i) { for (int i = 0; i < config->index.threads; ++i) {
WorkThread::StartThread("indexer" + std::to_string(i), [=]() { StartThread("indexer" + std::to_string(i), [=]() {
Indexer_Main(config, diag_engine, file_consumer_shared, Indexer_Main(config, diag_engine, file_consumer_shared,
timestamp_manager, import_manager, timestamp_manager, import_manager,
import_pipeline_status, project, working_files, waiter); import_pipeline_status, project, working_files, waiter);

View File

@ -72,9 +72,8 @@ struct Handler_TextDocumentDidOpen
clang_complete->FlushSession(entry.filename); clang_complete->FlushSession(entry.filename);
LOG_S(INFO) << "Flushed clang complete sessions for " << entry.filename; LOG_S(INFO) << "Flushed clang complete sessions for " << entry.filename;
if (params.args.size()) { if (params.args.size())
project->SetFlagsForFile(params.args, path); project->SetFlagsForFile(params.args, path);
}
} }
}; };
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen); REGISTER_MESSAGE_HANDLER(Handler_TextDocumentDidOpen);

View File

@ -1,62 +0,0 @@
#include "clang_format.h"
#include "message_handler.h"
#include "queue_manager.h"
#include "working_files.h"
#include <loguru.hpp>
namespace {
MethodType kMethodType = "textDocument/formatting";
struct In_TextDocumentFormatting : public RequestInMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
lsTextDocumentIdentifier textDocument;
lsFormattingOptions options;
};
Params params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentFormatting::Params, textDocument, options);
MAKE_REFLECT_STRUCT(In_TextDocumentFormatting, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentFormatting);
struct Out_TextDocumentFormatting
: public lsOutMessage<Out_TextDocumentFormatting> {
lsRequestId id;
std::vector<lsTextEdit> result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentFormatting, jsonrpc, id, result);
struct Handler_TextDocumentFormatting
: BaseMessageHandler<In_TextDocumentFormatting> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentFormatting* request) override {
Out_TextDocumentFormatting response;
response.id = request->id;
#if USE_CLANG_CXX
QueryFile* file;
if (!FindFileOrFail(db, project, request->id,
request->params.textDocument.uri.GetPath(), &file)) {
return;
}
WorkingFile* working_file =
working_files->GetFileByFilename(file->def->path);
response.result = ConvertClangReplacementsIntoTextEdits(
working_file->buffer_content,
ClangFormatDocument(working_file, 0,
working_file->buffer_content.size(),
request->params.options));
#else
LOG_S(WARNING) << "You must compile ccls with --use-clang-cxx to use "
"textDocument/formatting.";
// TODO: Fallback to execute the clang-format binary?
response.result = {};
#endif
QueueManager::WriteStdout(kMethodType, response);
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentFormatting);
} // namespace

View File

@ -277,7 +277,7 @@ void TraceMe() {
// If the environment variable is defined, wait for a debugger. // If the environment variable is defined, wait for a debugger.
// In gdb, you need to invoke `signal SIGCONT` if you want ccls to continue // In gdb, you need to invoke `signal SIGCONT` if you want ccls to continue
// after detaching. // after detaching.
if (getenv("CQUERY_TRACEME")) if (getenv("CCLS_TRACEME"))
raise(SIGTSTP); raise(SIGTSTP);
} }

View File

@ -5,6 +5,6 @@
void ccls_unreachable_internal(const char* msg, const char* file, int line) { void ccls_unreachable_internal(const char* msg, const char* file, int line) {
fprintf(stderr, "unreachable %s:%d %s\n", file, line, msg); fprintf(stderr, "unreachable %s:%d %s\n", file, line, msg);
CQUERY_BUILTIN_UNREACHABLE; CCLS_BUILTIN_UNREACHABLE;
abort(); abort();
} }

View File

@ -12,17 +12,17 @@
// TODO GCC // TODO GCC
#if __has_builtin(__builtin_unreachable) #if __has_builtin(__builtin_unreachable)
#define CQUERY_BUILTIN_UNREACHABLE __builtin_unreachable() #define CCLS_BUILTIN_UNREACHABLE __builtin_unreachable()
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define CQUERY_BUILTIN_UNREACHABLE __assume(false) #define CCLS_BUILTIN_UNREACHABLE __assume(false)
#else #else
#define CQUERY_BUILTIN_UNREACHABLE #define CCLS_BUILTIN_UNREACHABLE
#endif #endif
void ccls_unreachable_internal(const char* msg, const char* file, int line); void ccls_unreachable_internal(const char* msg, const char* file, int line);
#ifndef NDEBUG #ifndef NDEBUG
#define CQUERY_UNREACHABLE(msg) \ #define CCLS_UNREACHABLE(msg) \
ccls_unreachable_internal(msg, __FILE__, __LINE__) ccls_unreachable_internal(msg, __FILE__, __LINE__)
#else #else
#define CQUERY_UNREACHABLE(msg) #define CCLS_UNREACHABLE(msg)
#endif #endif

View File

@ -2,6 +2,7 @@
#include "cache_manager.h" #include "cache_manager.h"
#include "clang_utils.h" #include "clang_utils.h"
#include "filesystem.hh"
#include "language.h" #include "language.h"
#include "match.h" #include "match.h"
#include "platform.h" #include "platform.h"
@ -27,41 +28,22 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
extern bool gTestOutputMode;
struct CompileCommandsEntry { struct CompileCommandsEntry {
std::string directory; fs::path directory;
std::string file; std::string file;
std::string command; std::string command;
std::vector<std::string> args; std::vector<std::string> args;
fs::path ResolveIfRelative(fs::path path) const {
if (path.is_absolute())
return path;
return directory / path;
}
}; };
MAKE_REFLECT_STRUCT(CompileCommandsEntry, directory, file, command, args); MAKE_REFLECT_STRUCT(CompileCommandsEntry, directory, file, command, args);
namespace { namespace {
bool g_disable_normalize_path_for_test = false;
std::string NormalizePathWithTestOptOut(const std::string& path) {
if (g_disable_normalize_path_for_test) {
// Add a & so we can test to verify a path is normalized.
return "&" + path;
}
return NormalizePath(path);
}
bool IsUnixAbsolutePath(const std::string& path) {
return !path.empty() && path[0] == '/';
}
bool IsWindowsAbsolutePath(const std::string& path) {
auto is_drive_letter = [](char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
};
return path.size() > 3 && path[1] == ':' &&
(path[2] == '/' || path[2] == '\\') && is_drive_letter(path[0]);
}
enum class ProjectMode { CompileCommandsJson, DotCcls, ExternalCommand }; enum class ProjectMode { CompileCommandsJson, DotCcls, ExternalCommand };
struct ProjectConfig { struct ProjectConfig {
@ -127,22 +109,8 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
Config* init_opts, Config* init_opts,
ProjectConfig* config, ProjectConfig* config,
const CompileCommandsEntry& entry) { const CompileCommandsEntry& entry) {
auto cleanup_maybe_relative_path = [&](const std::string& path) {
// TODO/FIXME: Normalization will fail for paths that do not exist. Should
// it return an std::optional<std::string>?
assert(!path.empty());
if (entry.directory.empty() || IsUnixAbsolutePath(path) ||
IsWindowsAbsolutePath(path)) {
// We still want to normalize, as the path may contain .. characters.
return NormalizePathWithTestOptOut(path);
}
if (EndsWith(entry.directory, "/"))
return NormalizePathWithTestOptOut(entry.directory + path);
return NormalizePathWithTestOptOut(entry.directory + "/" + path);
};
Project::Entry result; Project::Entry result;
result.filename = NormalizePathWithTestOptOut(entry.file); result.filename = entry.file;
const std::string base_name = GetBaseName(entry.file); const std::string base_name = GetBaseName(entry.file);
// Expand %c %cpp %clang // Expand %c %cpp %clang
@ -185,7 +153,7 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
// Add -working-directory if not provided. // Add -working-directory if not provided.
if (!AnyStartsWith(args, "-working-directory")) if (!AnyStartsWith(args, "-working-directory"))
result.args.emplace_back("-working-directory=" + entry.directory); result.args.emplace_back("-working-directory=" + entry.directory.string());
bool next_flag_is_path = false; bool next_flag_is_path = false;
bool add_next_flag_to_quote_dirs = false; bool add_next_flag_to_quote_dirs = false;
@ -212,7 +180,7 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
// Finish processing path for the previous argument, which was a switch. // Finish processing path for the previous argument, which was a switch.
// {"-I", "foo"} style. // {"-I", "foo"} style.
if (next_flag_is_path) { if (next_flag_is_path) {
std::string normalized_arg = cleanup_maybe_relative_path(arg); std::string normalized_arg = entry.ResolveIfRelative(arg);
if (add_next_flag_to_quote_dirs) if (add_next_flag_to_quote_dirs)
config->quote_dirs.insert(normalized_arg); config->quote_dirs.insert(normalized_arg);
if (add_next_flag_to_angle_dirs) if (add_next_flag_to_angle_dirs)
@ -238,7 +206,7 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
if (StartsWith(arg, flag_type)) { if (StartsWith(arg, flag_type)) {
std::string path = arg.substr(flag_type.size()); std::string path = arg.substr(flag_type.size());
assert(!path.empty()); assert(!path.empty());
path = cleanup_maybe_relative_path(path); path = entry.ResolveIfRelative(path);
if (clang_cl || StartsWithAny(arg, kNormalizePathArgs)) if (clang_cl || StartsWithAny(arg, kNormalizePathArgs))
arg = flag_type + path; arg = flag_type + path;
if (ShouldAddToQuoteIncludes(flag_type)) if (ShouldAddToQuoteIncludes(flag_type))
@ -254,7 +222,7 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry(
// slow. See // slow. See
// https://github.com/cquery-project/cquery/commit/af63df09d57d765ce12d40007bf56302a0446678. // https://github.com/cquery-project/cquery/commit/af63df09d57d765ce12d40007bf56302a0446678.
if (EndsWith(arg, base_name)) if (EndsWith(arg, base_name))
arg = cleanup_maybe_relative_path(arg); arg = entry.ResolveIfRelative(arg);
// TODO Exclude .a .o to make link command in compile_commands.json work. // TODO Exclude .a .o to make link command in compile_commands.json work.
// Also, clang_parseTranslationUnit2FullArgv does not seem to accept // Also, clang_parseTranslationUnit2FullArgv does not seem to accept
// multiple source filenames. // multiple source filenames.
@ -450,13 +418,7 @@ std::vector<Project::Entry> LoadCompilationEntriesFromDirectory(
our_time.Resume(); our_time.Resume();
entry.directory = directory; entry.directory = directory;
std::string absolute_filename; entry.file = entry.ResolveIfRelative(relative_filename);
if (IsUnixAbsolutePath(relative_filename) ||
IsWindowsAbsolutePath(relative_filename))
absolute_filename = relative_filename;
else
absolute_filename = directory + "/" + relative_filename;
entry.file = NormalizePathWithTestOptOut(absolute_filename);
result.push_back( result.push_back(
GetCompilationEntryFromCompileCommandEntry(config, project, entry)); GetCompilationEntryFromCompileCommandEntry(config, project, entry));
@ -513,8 +475,8 @@ void Project::Load(Config* config, const std::string& root_directory) {
} }
// Setup project entries. // Setup project entries.
absolute_path_to_entry_index_.resize(entries.size()); absolute_path_to_entry_index_.reserve(entries.size());
for (int i = 0; i < entries.size(); ++i) for (size_t i = 0; i < entries.size(); ++i)
absolute_path_to_entry_index_[entries[i].filename] = i; absolute_path_to_entry_index_[entries[i].filename] = i;
} }
@ -616,9 +578,6 @@ TEST_SUITE("Project") {
void CheckFlags(const std::string& directory, const std::string& file, void CheckFlags(const std::string& directory, const std::string& file,
std::vector<std::string> raw, std::vector<std::string> raw,
std::vector<std::string> expected) { std::vector<std::string> expected) {
g_disable_normalize_path_for_test = true;
gTestOutputMode = true;
Config config; Config config;
ProjectConfig project; ProjectConfig project;
project.project_dir = "/w/c/s/"; project.project_dir = "/w/c/s/";
@ -656,7 +615,7 @@ TEST_SUITE("Project") {
CheckFlags( CheckFlags(
/* raw */ {"clang", "-lstdc++", "myfile.cc"}, /* raw */ {"clang", "-lstdc++", "myfile.cc"},
/* expected */ /* expected */
{"clang", "-working-directory=/dir/", "-lstdc++", "&/dir/myfile.cc", {"clang", "-working-directory=/dir/", "-lstdc++", "/dir/myfile.cc",
"-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option", "-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option",
"-fparse-all-comments"}); "-fparse-all-comments"});
@ -668,17 +627,18 @@ TEST_SUITE("Project") {
"-fparse-all-comments"}); "-fparse-all-comments"});
} }
#ifdef _WIN32
TEST_CASE("Windows path normalization") { TEST_CASE("Windows path normalization") {
CheckFlags("E:/workdir", "E:/workdir/bar.cc", /* raw */ {"clang", "bar.cc"}, CheckFlags("E:/workdir", "E:/workdir/bar.cc", /* raw */ {"clang", "bar.cc"},
/* expected */ /* expected */
{"clang", "-working-directory=E:/workdir", "&E:/workdir/bar.cc", {"clang", "-working-directory=E:/workdir", "E:/workdir/bar.cc",
"-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option", "-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option",
"-fparse-all-comments"}); "-fparse-all-comments"});
CheckFlags("E:/workdir", "E:/workdir/bar.cc", CheckFlags("E:/workdir", "E:/workdir/bar.cc",
/* raw */ {"clang", "E:/workdir/bar.cc"}, /* raw */ {"clang", "E:/workdir/bar.cc"},
/* expected */ /* expected */
{"clang", "-working-directory=E:/workdir", "&E:/workdir/bar.cc", {"clang", "-working-directory=E:/workdir", "E:/workdir/bar.cc",
"-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option", "-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option",
"-fparse-all-comments"}); "-fparse-all-comments"});
@ -686,7 +646,7 @@ TEST_SUITE("Project") {
/* raw */ {"clang-cl.exe", "/I./test", "E:/workdir/bar.cc"}, /* raw */ {"clang-cl.exe", "/I./test", "E:/workdir/bar.cc"},
/* expected */ /* expected */
{"clang-cl.exe", "-working-directory=E:/workdir", {"clang-cl.exe", "-working-directory=E:/workdir",
"/I&E:/workdir/./test", "&E:/workdir/bar.cc", "/I&E:/workdir/./test", "E:/workdir/bar.cc",
"-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option", "-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option",
"-fparse-all-comments"}); "-fparse-all-comments"});
@ -696,28 +656,20 @@ TEST_SUITE("Project") {
/* expected */ /* expected */
{"cl.exe", "-working-directory=E:/workdir", {"cl.exe", "-working-directory=E:/workdir",
"/I&E:/workdir/../third_party/test/include", "/I&E:/workdir/../third_party/test/include",
"&E:/workdir/bar.cc", "-resource-dir=/w/resource_dir/", "E:/workdir/bar.cc", "-resource-dir=/w/resource_dir/",
"-Wno-unknown-warning-option", "-fparse-all-comments"}); "-Wno-unknown-warning-option", "-fparse-all-comments"});
} }
#endif
TEST_CASE("Path in args") { TEST_CASE("Path in args") {
CheckFlags("/home/user", "/home/user/foo/bar.c", CheckFlags("/home/user", "/home/user/foo/bar.c",
/* raw */ {"cc", "-O0", "foo/bar.c"}, /* raw */ {"cc", "-O0", "foo/bar.c"},
/* expected */ /* expected */
{"cc", "-working-directory=/home/user", "-O0", {"cc", "-working-directory=/home/user", "-O0",
"&/home/user/foo/bar.c", "-resource-dir=/w/resource_dir/", "/home/user/foo/bar.c", "-resource-dir=/w/resource_dir/",
"-Wno-unknown-warning-option", "-fparse-all-comments"}); "-Wno-unknown-warning-option", "-fparse-all-comments"});
} }
TEST_CASE("Implied binary") {
CheckFlags("/home/user", "/home/user/foo/bar.cc",
/* raw */ {"clang", "-DDONT_IGNORE_ME"},
/* expected */
{"clang", "-working-directory=/home/user", "-DDONT_IGNORE_ME",
"-resource-dir=/w/resource_dir/", "-Wno-unknown-warning-option",
"-fparse-all-comments"});
}
TEST_CASE("Directory extraction") { TEST_CASE("Directory extraction") {
Config init_opts; Config init_opts;
ProjectConfig config; ProjectConfig config;
@ -752,11 +704,11 @@ TEST_SUITE("Project") {
GetCompilationEntryFromCompileCommandEntry(&init_opts, &config, entry); GetCompilationEntryFromCompileCommandEntry(&init_opts, &config, entry);
std::unordered_set<std::string> angle_expected{ std::unordered_set<std::string> angle_expected{
"&/a_absolute1", "&/a_absolute2", "&/base/a_relative1", "/a_absolute1", "/a_absolute2", "/base/a_relative1",
"&/base/a_relative2"}; "/base/a_relative2"};
std::unordered_set<std::string> quote_expected{ std::unordered_set<std::string> quote_expected{
"&/q_absolute1", "&/q_absolute2", "&/base/q_relative1", "/q_absolute1", "/q_absolute2", "/base/q_relative1",
"&/base/q_relative2"}; "/base/q_relative2"};
REQUIRE(config.angle_dirs == angle_expected); REQUIRE(config.angle_dirs == angle_expected);
REQUIRE(config.quote_dirs == quote_expected); REQUIRE(config.quote_dirs == quote_expected);
} }
@ -817,23 +769,6 @@ TEST_SUITE("Project") {
} }
} }
TEST_CASE("IsWindowsAbsolutePath works correctly") {
REQUIRE(IsWindowsAbsolutePath("C:/Users/projects/"));
REQUIRE(IsWindowsAbsolutePath("C:/Users/projects"));
REQUIRE(IsWindowsAbsolutePath("C:/Users/projects"));
REQUIRE(IsWindowsAbsolutePath("C:\\Users\\projects"));
REQUIRE(IsWindowsAbsolutePath("C:\\\\Users\\\\projects"));
REQUIRE(IsWindowsAbsolutePath("c:\\\\Users\\\\projects"));
REQUIRE(IsWindowsAbsolutePath("A:\\\\Users\\\\projects"));
REQUIRE(!IsWindowsAbsolutePath("C:/"));
REQUIRE(!IsWindowsAbsolutePath("../abc/test"));
REQUIRE(!IsWindowsAbsolutePath("5:/test"));
REQUIRE(!IsWindowsAbsolutePath("ccls/project/file.cc"));
REQUIRE(!IsWindowsAbsolutePath(""));
REQUIRE(!IsWindowsAbsolutePath("/etc/linux/path"));
}
TEST_CASE("Entry inference prefers same file endings") { TEST_CASE("Entry inference prefers same file endings") {
Project p; Project p;
{ {

View File

@ -3,13 +3,12 @@
#include "config.h" #include "config.h"
#include "method.h" #include "method.h"
#include <optional>
#include <sparsepp/spp.h>
#include <variant>
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <optional>
#include <string> #include <string>
#include <unordered_map>
#include <variant>
#include <vector> #include <vector>
class QueueManager; class QueueManager;
@ -29,7 +28,7 @@ struct Project {
std::vector<std::string> angle_include_directories; std::vector<std::string> angle_include_directories;
std::vector<Entry> entries; std::vector<Entry> entries;
spp::sparse_hash_map<std::string, int> absolute_path_to_entry_index_; std::unordered_map<std::string, int> absolute_path_to_entry_index_;
// Loads a project for the given |directory|. // Loads a project for the given |directory|.
// //

View File

@ -270,7 +270,7 @@ struct QueryDatabase {
std::vector<QueryVar> vars; std::vector<QueryVar> vars;
// Lookup symbol based on a usr. // Lookup symbol based on a usr.
spp::sparse_hash_map<std::string, QueryFileId> usr_to_file; std::unordered_map<std::string, QueryFileId> usr_to_file;
spp::sparse_hash_map<Usr, QueryTypeId> usr_to_type; spp::sparse_hash_map<Usr, QueryTypeId> usr_to_type;
spp::sparse_hash_map<Usr, QueryFuncId> usr_to_func; spp::sparse_hash_map<Usr, QueryFuncId> usr_to_func;
spp::sparse_hash_map<Usr, QueryVarId> usr_to_var; spp::sparse_hash_map<Usr, QueryVarId> usr_to_var;

View File

@ -16,6 +16,7 @@
#include <queue> #include <queue>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <thread>
#include <unordered_map> #include <unordered_map>
using namespace std::placeholders; using namespace std::placeholders;
@ -277,6 +278,13 @@ std::string GetDefaultResourceDirectory() {
return NormalizePath(result); return NormalizePath(result);
} }
void StartThread(const std::string& thread_name, std::function<void()> entry) {
new std::thread([thread_name, entry]() {
SetCurrentThreadName(thread_name);
entry();
});
}
TEST_SUITE("StripFileType") { TEST_SUITE("StripFileType") {
TEST_CASE("all") { TEST_CASE("all") {
REQUIRE(StripFileType("") == ""); REQUIRE(StripFileType("") == "");

View File

@ -149,3 +149,5 @@ inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
} }
std::string GetDefaultResourceDirectory(); std::string GetDefaultResourceDirectory();
void StartThread(const std::string& thread_name, std::function<void()> entry);

View File

@ -1,12 +0,0 @@
#include "work_thread.h"
#include "platform.h"
// static
void WorkThread::StartThread(const std::string& thread_name,
std::function<void()> entry_point) {
new std::thread([thread_name, entry_point]() {
SetCurrentThreadName(thread_name);
entry_point();
});
}

View File

@ -1,19 +0,0 @@
#pragma once
#include <atomic>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
// Helper methods for starting threads that do some work. Enables test code to
// wait for all work to complete.
struct WorkThread {
// Launch a new thread. |entry_point| will be called continously. It should
// return true if it there is still known work to be done.
static void StartThread(const std::string& thread_name,
std::function<void()> entry_point);
// Static-only class.
WorkThread() = delete;
};