diff --git a/.gitmodules b/.gitmodules index 27ff39f0..f0c27341 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "third_party/doctest"] path = third_party/doctest url = https://github.com/onqtam/doctest -[submodule "third_party/sparsepp"] - path = third_party/sparsepp - url = https://github.com/greg7mdp/sparsepp [submodule "third_party/loguru"] path = third_party/loguru url = https://github.com/emilk/loguru diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e71d938..010258ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,10 +91,9 @@ if(NOT SYSTEM_CLANG) target_compile_options(ccls PRIVATE -nostdinc++ -cxx-isystem ${CLANG_ROOT}/include/c++/v1) if(${CMAKE_SYSTEM_NAME} STREQUAL Linux) # Don't use -stdlib=libc++ because while ccls is linked with libc++, bundled clang+llvm require libstdc++ - target_link_libraries(ccls PRIVATE -L${CLANG_ROOT}/lib c++ c++experimental c++abi) - else() - # FreeBSD uses system libcxxrt.a and does not need libc++abi. - target_link_libraries(ccls PRIVATE -stdlib=libc++ -L${CLANG_ROOT}/lib c++experimental) + target_link_libraries(ccls PRIVATE -L${CLANG_ROOT}/lib c++ c++abi) + + # FreeBSD defaults to -stdlib=libc++ and uses system libcxxrt.a endif() endif() @@ -121,25 +120,17 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) target_link_libraries(ccls PRIVATE -lc++experimental) elseif(${CMAKE_SYSTEM_NAME} STREQUAL Linux) - if(NOT CLANG_USE_BUNDLED_LIBC++) - target_link_libraries(ccls PRIVATE -lstdc++fs) - endif() # loguru calls dladdr target_link_libraries(ccls PRIVATE ${CMAKE_DL_LIBS}) elseif(${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD) # loguru::stacktrace_as_stdstring calls backtrace_symbols - # sparsepp/spp_memory.h uses libkvm # src/platform_posix.cc uses libthr find_package(Backtrace REQUIRED) - target_link_libraries(ccls PRIVATE ${Backtrace_LIBRARIES} kvm thr) + target_link_libraries(ccls PRIVATE ${Backtrace_LIBRARIES} thr) if(SYSTEM_CLANG) target_link_libraries(ccls PRIVATE c++experimental) endif() - -elseif(${CMAKE_SYSTEM_NAME} STREQUAL Windows) - # sparsepp/spp_memory.h uses LibPsapi - target_link_libraries(ccls PRIVATE Psapi) endif() ### Definitions @@ -156,7 +147,6 @@ target_include_directories(ccls PRIVATE src third_party third_party/rapidjson/include - third_party/sparsepp third_party/loguru third_party/doctest) diff --git a/cmake/FindClang.cmake b/cmake/FindClang.cmake index ab81ad61..9524ca23 100644 --- a/cmake/FindClang.cmake +++ b/cmake/FindClang.cmake @@ -70,6 +70,7 @@ set(_Clang_REQUIRED_VARS Clang_LIBRARY Clang_INCLUDE_DIR Clang_EXECUTABLE _Clang_find_library(Clang_LIBRARY clang) _Clang_find_add_library(clangDriver) +_Clang_find_add_library(clangBasic) _Clang_find_add_library(LLVMOption) _Clang_find_add_library(LLVMSupport) _Clang_find_add_library(LLVMDemangle) diff --git a/src/clang_complete.cc b/src/clang_complete.cc index 8ea334a8..6d23be43 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -1,10 +1,12 @@ #include "clang_complete.h" #include "clang_utils.h" -#include "filesystem.hh" #include "platform.h" #include "timer.h" +#include "filesystem.hh" +using namespace llvm; + #include #include @@ -29,8 +31,9 @@ unsigned Flags() { } std::string StripFileType(const std::string& path) { - fs::path p(path); - return p.parent_path() / p.stem(); + SmallString<128> Ret; + sys::path::append(Ret, sys::path::parent_path(path), sys::path::stem(path)); + return Ret.str(); } unsigned GetCompletionPriority(const CXCompletionString& str, diff --git a/src/clang_utils.cc b/src/clang_utils.cc index 5a25106a..6a56d219 100644 --- a/src/clang_utils.cc +++ b/src/clang_utils.cc @@ -1,8 +1,10 @@ #include "clang_utils.h" -#include "filesystem.hh" #include "platform.h" +#include "filesystem.hh" +using namespace llvm; + namespace { lsRange GetLsRangeForFixIt(const CXSourceRange& range) { @@ -115,8 +117,11 @@ std::string FileName(CXFile file) { // clang_getFileName return values may contain .. ret = NormalizePath(ToString(clang_getFileName(file))); // Resolve /usr/include/c++/7.3.0 symlink. - if (!StartsWith(ret, g_config->projectRoot)) - ret = fs::canonical(ret); + if (!StartsWith(ret, g_config->projectRoot)) { + SmallString<256> dest; + sys::fs::real_path(ret, dest); + ret = dest.str(); + } return ret; } diff --git a/src/filesystem.cc b/src/filesystem.cc index 541bdfcb..f9d31004 100644 --- a/src/filesystem.cc +++ b/src/filesystem.cc @@ -1,8 +1,8 @@ #include "filesystem.hh" +using namespace llvm; #include "utils.h" -#include #include static void GetFilesInFolderHelper( @@ -10,31 +10,33 @@ static void GetFilesInFolderHelper( bool recursive, std::string output_prefix, const std::function& handler) { - std::queue> q; - q.emplace(fs::path(folder), fs::path(output_prefix)); - while (!q.empty()) { - try { - for (auto it = fs::directory_iterator(q.front().first); - it != fs::directory_iterator(); ++it) { - auto path = it->path(); - std::string filename = path.filename(); - if (filename[0] != '.' || filename == ".ccls") { - fs::file_status status = it->symlink_status(); - if (fs::is_regular_file(status)) - handler(q.front().second / filename); - else if (fs::is_directory(status) || fs::is_symlink(status)) { - if (recursive) { - std::string child_dir = q.front().second / filename; - if (fs::is_directory(status)) - q.push(make_pair(path, child_dir)); - } - } - } + std::error_code ec; + if (recursive) + for (sys::fs::recursive_directory_iterator I(folder, ec), E; I != E && !ec; + I.increment(ec)) { + std::string path = I->path(), filename = sys::path::filename(path); + if (filename[0] != '.' || filename == ".ccls") { + SmallString<256> Path; + if (output_prefix.size()) { + sys::path::append(Path, output_prefix, path); + handler(Path.str()); + } else + handler(path); + } + } + else + for (sys::fs::directory_iterator I(folder, ec), E; I != E && !ec; + I.increment(ec)) { + std::string path = I->path(), filename = sys::path::filename(path); + if (filename[0] != '.' || filename == ".ccls") { + SmallString<256> Path; + if (output_prefix.size()) { + sys::path::append(Path, output_prefix, path); + handler(Path.str()); + } else + handler(path); } - } catch (fs::filesystem_error&) { } - q.pop(); - } } void GetFilesInFolder(std::string folder, diff --git a/src/filesystem.hh b/src/filesystem.hh index dedce15e..334810fc 100644 --- a/src/filesystem.hh +++ b/src/filesystem.hh @@ -1,11 +1,11 @@ #pragma once -#include +#include +#include + #include #include -namespace fs = std::experimental::filesystem; - void GetFilesInFolder(std::string folder, bool recursive, bool add_folder_to_path, diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index 6dfb3d3e..a2d21e41 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -1,6 +1,5 @@ #include "cache_manager.h" #include "diagnostics_engine.h" -#include "filesystem.hh" #include "import_pipeline.h" #include "include_complete.h" #include "message_handler.h" @@ -11,6 +10,9 @@ #include "timer.h" #include "working_files.h" +#include "filesystem.hh" +using namespace llvm; + #include #include @@ -492,10 +494,10 @@ struct Handler_Initialize : BaseMessageHandler { config->projectRoot = project_path; // Create two cache directories for files inside and outside of the // project. - fs::create_directories(config->cacheDirectory + - EscapeFileName(config->projectRoot)); - fs::create_directories(config->cacheDirectory + '@' + - EscapeFileName(config->projectRoot)); + sys::fs::create_directories(config->cacheDirectory + + EscapeFileName(config->projectRoot)); + sys::fs::create_directories(config->cacheDirectory + '@' + + EscapeFileName(config->projectRoot)); g_config = std::move(config); Timer time; diff --git a/src/project.cc b/src/project.cc index 67c7f0a0..63ed720e 100644 --- a/src/project.cc +++ b/src/project.cc @@ -35,15 +35,17 @@ using namespace llvm::opt; #include struct CompileCommandsEntry { - fs::path directory; + std::string directory; std::string file; std::string command; std::vector args; - fs::path ResolveIfRelative(fs::path path) const { - if (path.is_absolute()) + std::string ResolveIfRelative(std::string path) const { + if (sys::path::is_absolute(path)) return path; - return directory / path; + SmallString<256> Ret; + sys::path::append(Ret, directory, path); + return Ret.str(); } }; MAKE_REFLECT_STRUCT(CompileCommandsEntry, directory, file, command, args); @@ -56,7 +58,7 @@ struct ProjectConfig { std::unordered_set quote_dirs; std::unordered_set angle_dirs; std::vector extra_flags; - fs::path project_dir; + std::string project_dir; ProjectMode mode = ProjectMode::CompileCommandsJson; }; @@ -72,7 +74,7 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry( const CompileCommandsEntry& entry) { Project::Entry result; result.filename = entry.file; - const std::string base_name = fs::path(entry.file).filename(); + const std::string base_name = sys::path::filename(entry.file); // Expand %c %cpp %clang std::vector args; @@ -145,7 +147,7 @@ Project::Entry GetCompilationEntryFromCompileCommandEntry( if (!Args.hasArg(OPT_resource_dir)) args.push_back("-resource-dir=" + g_config->clang.resourceDir); if (!Args.hasArg(OPT_working_directory)) - args.push_back("-working-directory=" + entry.directory.string()); + args.push_back("-working-directory=" + entry.directory); // There could be a clang version mismatch between what the project uses and // what ccls uses. Make sure we do not emit warnings for mismatched options. @@ -176,10 +178,11 @@ std::vector ReadCompilerArgumentsFromFile( std::vector LoadFromDirectoryListing(ProjectConfig* config) { std::vector result; config->mode = ProjectMode::DotCcls; - LOG_IF_S(WARNING, !fs::exists(config->project_dir / ".ccls") && - config->extra_flags.empty()) - << "ccls has no clang arguments. Considering adding either a " - "compile_commands.json or .ccls file. See the ccls README for " + SmallString<256> Path; + sys::path::append(Path, config->project_dir, ".ccls"); + LOG_IF_S(WARNING, !sys::fs::exists(Path) && config->extra_flags.empty()) + << "ccls has no clang arguments. Use either " + "compile_commands.json or .ccls, See ccls README for " "more information."; std::unordered_map> folder_args; @@ -190,21 +193,20 @@ std::vector LoadFromDirectoryListing(ProjectConfig* config) { [&folder_args, &files](const std::string& path) { if (SourceFileLanguage(path) != LanguageId::Unknown) { files.push_back(path); - } else if (fs::path(path).filename() == ".ccls") { + } else if (sys::path::filename(path) == ".ccls") { LOG_S(INFO) << "Using .ccls arguments from " << path; - folder_args.emplace( - fs::path(path).parent_path().string(), - ReadCompilerArgumentsFromFile(path)); + folder_args.emplace(sys::path::parent_path(path), + ReadCompilerArgumentsFromFile(path)); } }); - const std::string project_dir = config->project_dir.string(); + const std::string& project_dir = config->project_dir; const auto& project_dir_args = folder_args[project_dir]; LOG_IF_S(INFO, !project_dir_args.empty()) << "Using .ccls arguments " << StringJoin(project_dir_args); - auto GetCompilerArgumentForFile = [&project_dir, &folder_args](fs::path cur) { - while (!(cur = cur.parent_path()).empty()) { + auto GetCompilerArgumentForFile = [&project_dir, &folder_args](std::string cur) { + while (!(cur = sys::path::parent_path(cur)).empty()) { auto it = folder_args.find(cur); if (it != folder_args.end()) return it->second; @@ -235,16 +237,20 @@ std::vector LoadCompilationEntriesFromDirectory( ProjectConfig* project, const std::string& opt_compilation_db_dir) { // If there is a .ccls file always load using directory listing. - if (fs::exists(project->project_dir / ".ccls")) + SmallString<256> Path; + sys::path::append(Path, project->project_dir, ".ccls"); + if (sys::fs::exists(Path)) return LoadFromDirectoryListing(project); // If |compilationDatabaseCommand| is specified, execute it to get the compdb. - fs::path comp_db_dir; + std::string comp_db_dir; + Path.clear(); if (g_config->compilationDatabaseCommand.empty()) { project->mode = ProjectMode::CompileCommandsJson; // Try to load compile_commands.json, but fallback to a project listing. - comp_db_dir = opt_compilation_db_dir.empty() ? project->project_dir.string() + comp_db_dir = opt_compilation_db_dir.empty() ? project->project_dir : opt_compilation_db_dir; + sys::path::append(Path, comp_db_dir, "compile_commands.json"); } else { project->mode = ProjectMode::ExternalCommand; #ifdef _WIN32 @@ -254,6 +260,7 @@ std::vector LoadCompilationEntriesFromDirectory( if (!mkdtemp(tmpdir)) return {}; comp_db_dir = tmpdir; + sys::path::append(Path, comp_db_dir, "compile_commands.json"); rapidjson::StringBuffer input; rapidjson::Writer writer(input); JsonWriter json_writer(&writer); @@ -262,14 +269,12 @@ std::vector LoadCompilationEntriesFromDirectory( std::vector{g_config->compilationDatabaseCommand, project->project_dir}, input.GetString()); - FILE* fout = fopen((comp_db_dir / "compile_commands.json").c_str(), "wb"); + FILE* fout = fopen(Path.c_str(), "wb"); fwrite(contents.c_str(), contents.size(), 1, fout); fclose(fout); #endif } - fs::path comp_db_path = comp_db_dir / "compile_commands.json"; - LOG_S(INFO) << "Trying to load " << comp_db_path.string(); CXCompilationDatabase_Error cx_db_load_error; CXCompilationDatabase cx_db = clang_CompilationDatabase_fromDirectory( comp_db_dir.c_str(), &cx_db_load_error); @@ -277,17 +282,18 @@ std::vector LoadCompilationEntriesFromDirectory( #ifdef _WIN32 // TODO #else - unlink(comp_db_path.c_str()); + unlink(Path.c_str()); rmdir(comp_db_dir.c_str()); #endif } - if (cx_db_load_error == CXCompilationDatabase_CanNotLoadDatabase) { - LOG_S(INFO) << "Unable to load " << comp_db_path.string() + LOG_S(INFO) << "unable to load " << Path.c_str() << "; using directory listing instead."; return LoadFromDirectoryListing(project); } + LOG_S(INFO) << "loaded " << Path.c_str(); + Timer clang_time; Timer our_time; clang_time.Pause(); @@ -437,13 +443,12 @@ Project::Entry Project::FindCompilationEntryForFile( // |best_entry| probably has its own path in the arguments. We need to remap // that path to the new filename. - fs::path best_entry_base_name = fs::path(best_entry->filename).filename(); + std::string best_entry_base_name = sys::path::filename(best_entry->filename); for (std::string& arg : result.args) { try { if (arg == best_entry->filename || - fs::path(arg).filename() == best_entry_base_name) { + sys::path::filename(arg) == best_entry_base_name) arg = filename; - } } catch (...) { } } diff --git a/src/query.cc b/src/query.cc index 036f85e2..5ed2d77e 100644 --- a/src/query.cc +++ b/src/query.cc @@ -154,7 +154,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) { // Returns true if an element with the same file is found. template -bool TryReplaceDef(std::forward_list& def_list, Q&& def) { +bool TryReplaceDef(llvm::SmallVectorImpl& def_list, Q&& def) { for (auto& def1 : def_list) if (def1.spell->file_id == def.spell->file_id) { def1 = std::move(def); @@ -262,18 +262,22 @@ void QueryDatabase::RemoveUsrs( case SymbolKind::Func: { for (auto usr : to_remove) { QueryFunc& func = Func(usr); - func.def.remove_if([=](const QueryFunc::Def& def) { + auto it = llvm::find_if(func.def, [=](const QueryFunc::Def& def) { return def.spell->file_id == file_id; }); + if (it != func.def.end()) + func.def.erase(it); } break; } case SymbolKind::Var: { for (auto usr : to_remove) { QueryVar& var = Var(usr); - var.def.remove_if([=](const QueryVar::Def& def) { + auto it = llvm::find_if(var.def, [=](const QueryVar::Def& def) { return def.spell->file_id == file_id; }); + if (it != var.def.end()) + var.def.erase(it); } break; } @@ -348,7 +352,7 @@ void QueryDatabase::Update(int file_id, QueryFunc& existing = Func(u.first); existing.usr = u.first; if (!TryReplaceDef(existing.def, std::move(def))) { - existing.def.push_front(std::move(def)); + existing.def.push_back(std::move(def)); UpdateSymbols(&existing.symbol_idx, SymbolKind::Func, u.first); } } @@ -364,7 +368,7 @@ void QueryDatabase::Update(int file_id, QueryType& existing = Type(u.first); existing.usr = u.first; if (!TryReplaceDef(existing.def, std::move(def))) { - existing.def.push_front(std::move(def)); + existing.def.push_back(std::move(def)); UpdateSymbols(&existing.symbol_idx, SymbolKind::Type, u.first); } } @@ -380,7 +384,7 @@ void QueryDatabase::Update(int file_id, QueryVar& existing = Var(u.first); existing.usr = u.first; if (!TryReplaceDef(existing.def, std::move(def))) { - existing.def.push_front(std::move(def)); + existing.def.push_back(std::move(def)); if (!existing.def.front().is_local()) UpdateSymbols(&existing.symbol_idx, SymbolKind::Var, u.first); } diff --git a/src/query.h b/src/query.h index 571f1a4e..f85d986b 100644 --- a/src/query.h +++ b/src/query.h @@ -3,9 +3,8 @@ #include "indexer.h" #include "serializer.h" -#include - -#include +#include +#include struct QueryFile; struct QueryType; @@ -69,7 +68,7 @@ using UsrUpdate = struct QueryFunc : QueryEntity { Usr usr; int symbol_idx = -1; - std::forward_list def; + llvm::SmallVector def; std::vector declarations; std::vector uses; std::vector derived; @@ -78,7 +77,7 @@ struct QueryFunc : QueryEntity { struct QueryType : QueryEntity { Usr usr; int symbol_idx = -1; - std::forward_list def; + llvm::SmallVector def; std::vector declarations; std::vector uses; std::vector derived; @@ -88,7 +87,7 @@ struct QueryType : QueryEntity { struct QueryVar : QueryEntity { Usr usr; int symbol_idx = -1; - std::forward_list def; + llvm::SmallVector def; std::vector declarations; std::vector uses; }; @@ -138,9 +137,9 @@ struct QueryDatabase { std::vector files; std::unordered_map name2file_id; - spp::sparse_hash_map usr2func; - spp::sparse_hash_map usr2type; - spp::sparse_hash_map usr2var; + llvm::DenseMap usr2func; + llvm::DenseMap usr2type; + llvm::DenseMap usr2var; // Marks the given Usrs as invalid. void RemoveUsrs(SymbolKind usr_kind, const std::vector& to_remove); diff --git a/src/query_utils.h b/src/query_utils.h index 59bbc427..3b57ae9d 100644 --- a/src/query_utils.h +++ b/src/query_utils.h @@ -11,7 +11,7 @@ Maybe GetDefinitionExtent(QueryDatabase* db, SymbolIdx sym); // Get defining declaration (if exists) or an arbitrary declaration (otherwise) // for each id. template -std::vector GetDeclarations(spp::sparse_hash_map& usr2entity, +std::vector GetDeclarations(llvm::DenseMap& usr2entity, const std::vector& usrs) { std::vector ret; ret.reserve(usrs.size()); @@ -135,7 +135,7 @@ void EachOccurrenceWithParent(QueryDatabase* db, } template -void EachDefinedEntity(spp::sparse_hash_map& collection, +void EachDefinedEntity(llvm::DenseMap& collection, const std::vector& usrs, Fn&& fn) { for (Usr usr : usrs) { diff --git a/src/serializer.cc b/src/serializer.cc index fd07a045..b98abbde 100644 --- a/src/serializer.cc +++ b/src/serializer.cc @@ -160,7 +160,7 @@ void Reflect(Writer& visitor, IndexInclude& value) { REFLECT_MEMBER_START(); REFLECT_MEMBER(line); if (gTestOutputMode) { - std::string basename = fs::path(value.resolved_path).filename(); + std::string basename = llvm::sys::path::filename(value.resolved_path); if (!StartsWith(value.resolved_path, "&")) basename = "&" + basename; REFLECT_MEMBER2("resolved_path", basename); diff --git a/src/utils.cc b/src/utils.cc index f9632f3a..57a5319f 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -1,6 +1,7 @@ #include "utils.h" #include "filesystem.hh" +using namespace llvm; #include "platform.h" #include @@ -137,12 +138,10 @@ void WriteToFile(const std::string& filename, const std::string& content) { } std::optional LastWriteTime(const std::string& filename) { - std::error_code ec; - auto ftime = fs::last_write_time(filename, ec); - if (ec) return std::nullopt; - return std::chrono::time_point_cast(ftime) - .time_since_epoch() - .count(); + sys::fs::file_status Status; + if (sys::fs::status(filename, Status)) + return {}; + return Status.getLastModificationTime().time_since_epoch().count(); } std::string GetDefaultResourceDirectory() { diff --git a/third_party/sparsepp b/third_party/sparsepp deleted file mode 160000 index 1ca7189f..00000000 --- a/third_party/sparsepp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1ca7189fe81ee8c59bf08196852f70843a68a63a