From c0ef47d5e0647f0d050598d93ea478161ddd869e Mon Sep 17 00:00:00 2001 From: firstlove Date: Sat, 23 Mar 2019 17:52:14 +0800 Subject: [PATCH] improvge and fix UB of the use of log https://github.com/MaskRay/ccls/blob/master/src/main.cc#L90 is not relyable because `1` and `2` are out of range of `enum Verbosity`. According to CWG 1766: > A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (10.2 [dcl.enum]). Otherwise, the resulting value is unspecified (and might not be in that range) behavior is undefined. so, stuff like LOG_V(1) and LOG_V(2) are UB. The solution is either: Message(Verbosity verbosity, const char* file, int line); -> Message(int verbosity, const char* file, int line); or ```c++ enum Verbosity { Verbosity_FATAL = -3, Verbosity_ERROR = -2, Verbosity_WARNING = -1, Verbosity_INFO = 0, // more level... }; ``` IMO the latter is better option. This pr also replace macros inside log.hh with short functions and replace `enum Verbosity` with `enum class Verbosity` --- src/indexer.cc | 27 +++++--- src/log.cc | 14 +++-- src/log.hh | 52 ++++++++-------- src/lsp.cc | 7 ++- src/messages/initialize.cc | 36 ++++++----- src/messages/textDocument_completion.cc | 16 +++-- src/messages/workspace.cc | 11 ++-- src/pipeline.cc | 82 +++++++++++++++---------- src/project.cc | 79 ++++++++++++++---------- src/sema_manager.cc | 9 ++- src/serializer.cc | 13 ++-- src/utils.cc | 6 +- src/working_files.cc | 11 ++-- 13 files changed, 213 insertions(+), 150 deletions(-) diff --git a/src/indexer.cc b/src/indexer.cc index f4e9be8b..443e0c8e 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -37,6 +37,7 @@ limitations under the License. #include using namespace clang; +using namespace ccls::log; namespace ccls { namespace { @@ -525,8 +526,9 @@ public: } auto i = name.find(short_name); if (short_name.size()) - while (i != std::string::npos && ((i && isIdentifierBody(name[i - 1])) || - isIdentifierBody(name[i + short_name.size()]))) + while (i != std::string::npos && + ((i && isIdentifierBody(name[i - 1])) || + isIdentifierBody(name[i + short_name.size()]))) i = name.find(short_name, i + short_name.size()); if (i == std::string::npos) { // e.g. operator type-parameter-1 @@ -751,8 +753,8 @@ public: switch (D->getKind()) { case Decl::CXXConversion: // *operator* int => *operator int* case Decl::CXXDestructor: // *~*A => *~A* - case Decl::CXXMethod: // *operator*= => *operator=* - case Decl::Function: // operator delete + case Decl::CXXMethod: // *operator*= => *operator=* + case Decl::Function: // operator delete if (Loc.isFileID()) { SourceRange R = cast(OrigD)->getNameInfo().getSourceRange(); @@ -794,9 +796,9 @@ public: switch (kind) { case Kind::Invalid: if (ls_kind == SymbolKind::Unknown) - LOG_S(INFO) << "Unhandled " << int(D->getKind()) << " " - << info->qualified << " in " << db->path << ":" - << (loc.start.line + 1) << ":" << (loc.start.column + 1); + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "Unhandled ", int(D->getKind()), " ", info->qualified, " in ", + db->path, ":", (loc.start.line + 1), ":", (loc.start.column + 1)); return true; case Kind::File: return true; @@ -952,12 +954,14 @@ public: if (auto *RD = dyn_cast(D)) { if (type->def.detailed_name[0] == '\0' && info->short_name.empty()) { StringRef Tag; + // clang-format off switch (RD->getTagKind()) { case TTK_Struct: Tag = "struct"; break; case TTK_Interface: Tag = "__interface"; break; case TTK_Union: Tag = "union"; break; case TTK_Class: Tag = "class"; break; case TTK_Enum: Tag = "enum"; break; + // clang-format on } if (TypedefNameDecl *TD = RD->getTypedefNameForAnonDecl()) { StringRef Name = TD->getName(); @@ -1220,7 +1224,8 @@ Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, bool &ok) { ok = true; auto PCH = std::make_shared(); - llvm::IntrusiveRefCntPtr FS = llvm::vfs::getRealFileSystem(); + llvm::IntrusiveRefCntPtr FS = + llvm::vfs::getRealFileSystem(); std::shared_ptr CI = BuildCompilerInvocation(main, args, FS); // e.g. .s @@ -1288,12 +1293,14 @@ Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, ok = true; }; if (!CRC.RunSafely(parse)) { - LOG_S(ERROR) << "clang crashed for " << main; + if (auto v = Verbosity::ERROR; LogRequire(v)) + Log(v, "clang crashed for ", main); return {}; } } if (!ok) { - LOG_S(ERROR) << "failed to index " << main; + if (auto v = Verbosity::ERROR; LogRequire(v)) + Log(v, "failed to index ", main); return {}; } for (auto &Buf : Bufs) diff --git a/src/log.cc b/src/log.cc index 3e2b5592..ea909410 100644 --- a/src/log.cc +++ b/src/log.cc @@ -56,11 +56,13 @@ Message::Message(Verbosity verbosity, const char *file, int line) stream_ << ' '; // clang-format off switch (verbosity_) { - case Verbosity_FATAL: stream_ << 'F'; break; - case Verbosity_ERROR: stream_ << 'E'; break; - case Verbosity_WARNING: stream_ << 'W'; break; - case Verbosity_INFO: stream_ << 'I'; break; - default: stream_ << "V(" << int(verbosity_) << ')'; + case Verbosity::FATAL: stream_ << 'F'; break; + case Verbosity::ERROR: stream_ << 'E'; break; + case Verbosity::WARNING: stream_ << 'W'; break; + case Verbosity::INFO: stream_ << 'I'; break; + case Verbosity::DEBUG: stream_ << 'D'; break; + case Verbosity::VERBOSE: stream_ << 'V'; break; + default: stream_ << "V(" << +verbosity_ << ')'; } // clang-format on stream_ << ' '; @@ -73,7 +75,7 @@ Message::~Message() { stream_ << '\n'; fputs(stream_.str().c_str(), file); fflush(file); - if (verbosity_ == Verbosity_FATAL) + if (verbosity_ == Verbosity::FATAL) abort(); } } // namespace ccls::log diff --git a/src/log.hh b/src/log.hh index 5d06d41a..46ae6582 100644 --- a/src/log.hh +++ b/src/log.hh @@ -1,41 +1,45 @@ #pragma once -#include #include +#include +#include +#include namespace ccls::log { -extern FILE* file; +extern FILE *file; -struct Voidify { - void operator&(const std::ostream&) {} +enum class Verbosity : signed { + FATAL = -3, + ERROR = -2, + WARNING = -1, + INFO = 0, + DEBUG = 1, + VERBOSE = 2 }; +template > +constexpr auto operator+(T e) noexcept + -> std::enable_if_t, UT> { + return static_cast(e); +} -enum Verbosity { - Verbosity_FATAL = -3, - Verbosity_ERROR = -2, - Verbosity_WARNING = -1, - Verbosity_INFO = 0, -}; extern Verbosity verbosity; struct Message { std::stringstream stream_; - int verbosity_; + Verbosity verbosity_; - Message(Verbosity verbosity, const char* file, int line); + Message(Verbosity verbosity, const char *file, int line); ~Message(); }; + +template inline void Log(Verbosity v, Args const &... args) { + (Message(v, __FILE__, __LINE__).stream_ << ... << args); } -#define LOG_IF(v, cond) \ - !(cond) ? void(0) \ - : ccls::log::Voidify() & \ - ccls::log::Message(v, __FILE__, __LINE__).stream_ -#define LOG_S(v) \ - LOG_IF(ccls::log::Verbosity_##v, \ - ccls::log::Verbosity_##v <= ccls::log::verbosity) -#define LOG_IF_S(v, cond) \ - LOG_IF(ccls::log::Verbosity_##v, \ - (cond) && ccls::log::Verbosity_##v <= ccls::log::verbosity) -#define LOG_V_ENABLED(v) (v <= ccls::log::verbosity) -#define LOG_V(v) LOG_IF(ccls::log::Verbosity(v), LOG_V_ENABLED(v)) +// XXX: According to CWG 1766, static_cast invalid(out of range) value to enum +// class is UB +bool inline LogRequire(Verbosity v) { return +v <= +verbosity; } + +// ADL +bool inline LogIf(Verbosity v, bool cond) { return cond && LogRequire(v); } +} // namespace ccls::log diff --git a/src/lsp.cc b/src/lsp.cc index 5c56f324..738f278a 100644 --- a/src/lsp.cc +++ b/src/lsp.cc @@ -21,6 +21,7 @@ limitations under the License. #include #include +using namespace ccls::log; namespace ccls { void Reflect(JsonReader &vis, RequestId &v) { @@ -103,9 +104,9 @@ void DocumentUri::SetPath(const std::string &path) { std::string DocumentUri::GetPath() const { if (raw_uri.compare(0, 7, "file://")) { - LOG_S(WARNING) - << "Received potentially bad URI (not starting with file://): " - << raw_uri; + if (auto v = Verbosity::WARNING; LogRequire(v)) + Log(v, "Received potentially bad URI (not starting with file://): ", + raw_uri); return raw_uri; } std::string ret; diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index c0344a34..977b0406 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "sema_manager.hh" #include "filesystem.hh" #include "include_complete.hh" #include "log.hh" @@ -21,6 +20,7 @@ limitations under the License. #include "pipeline.hh" #include "platform.hh" #include "project.hh" +#include "sema_manager.hh" #include "working_files.hh" #include @@ -29,12 +29,13 @@ limitations under the License. #include #include -#include #include +#include #include namespace ccls { using namespace llvm; +using namespace ccls::log; extern std::vector g_init_options; @@ -184,7 +185,8 @@ REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol, hierarchicalDocumentSymbolSupport); REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport); REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation); -REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol, publishDiagnostics); +REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol, + publishDiagnostics); struct ClientCap { WorkspaceClientCap workspace; @@ -268,8 +270,9 @@ void *Indexer(void *arg_) { void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { std::string project_path = NormalizePath(param.rootUri->GetPath()); - LOG_S(INFO) << "initialize in directory " << project_path << " with uri " - << param.rootUri->raw_uri; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "initialize in directory ", project_path, " with uri ", + param.rootUri->raw_uri); { g_config = new Config(param.initializationOptions); @@ -291,7 +294,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { rapidjson::Writer writer(output); JsonWriter json_writer(&writer); Reflect(json_writer, *g_config); - LOG_S(INFO) << "initializationOptions: " << output.GetString(); + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "initializationOptions: ", output.GetString()); if (g_config->cache.directory.size()) { SmallString<256> Path(g_config->cache.directory); @@ -320,7 +324,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { if (g_config->clang.resourceDir.empty()) g_config->clang.resourceDir = GetDefaultResourceDirectory(); DoPathMapping(g_config->clang.resourceDir); - LOG_S(INFO) << "use -resource-dir=" << g_config->clang.resourceDir; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "use -resource-dir=", g_config->clang.resourceDir); // Send initialization before starting indexers, so we don't send a // status update too early. @@ -352,11 +357,12 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { std::sort(workspaceFolders.begin(), workspaceFolders.end(), [](auto &l, auto &r) { return l.first.size() > r.first.size(); }); for (auto &[folder, real] : workspaceFolders) - if (real.empty()) - LOG_S(INFO) << "workspace folder: " << folder; - else - LOG_S(INFO) << "workspace folder: " << folder << " -> " << real; - + if (auto v = Verbosity::INFO; LogRequire(v)) { + if (real.empty()) + Log(v, "workspace folder: ", folder); + else + Log(v, "workspace folder: ", folder, " -> ", real); + } if (g_config->cache.directory.empty()) g_config->cache.retainInMemory = 1; else if (!g_config->cache.hierarchicalPath) @@ -378,7 +384,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { if (g_config->index.threads == 0) g_config->index.threads = std::thread::hardware_concurrency(); - LOG_S(INFO) << "start " << g_config->index.threads << " indexers"; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "start ", g_config->index.threads, " indexers"); for (int i = 0; i < g_config->index.threads; i++) SpawnThread(Indexer, new std::pair{m, i}); @@ -386,7 +393,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { // files, because that takes a long time. m->include_complete->Rescan(); - LOG_S(INFO) << "dispatch initial index requests"; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "dispatch initial index requests"); m->project->Index(m->wfiles, reply.id); m->manager->sessions.SetCapacity(g_config->session.maxNum); diff --git a/src/messages/textDocument_completion.cc b/src/messages/textDocument_completion.cc index cf60847a..19f3960b 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -31,6 +31,7 @@ limitations under the License. namespace ccls { using namespace clang; using namespace llvm; +using namespace ccls::log; REFLECT_UNDERLYING(InsertTextFormat); REFLECT_UNDERLYING(CompletionItemKind); @@ -61,8 +62,7 @@ REFLECT_STRUCT(CompletionList, isIncomplete, items); #if LLVM_VERSION_MAJOR < 8 void DecorateIncludePaths(const std::smatch &match, - std::vector *items, - char quote) { + std::vector *items, char quote) { std::string spaces_after_include = " "; if (match[3].compare("include") == 0 && quote != '\0') spaces_after_include = match[4].str(); @@ -161,12 +161,15 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text, auto &edits = item.additionalTextEdits; if (overwrite_len > 0) { item.textEdit.range.start = overwrite_begin; - std::string orig = buffer_line.substr(overwrite_begin.character, overwrite_len); + std::string orig = + buffer_line.substr(overwrite_begin.character, overwrite_len); if (edits.size() && edits[0].range.end == begin_pos && edits[0].range.start.line == begin_pos.line) { - int cur_edit_len = edits[0].range.end.character - edits[0].range.start.character; + int cur_edit_len = + edits[0].range.end.character - edits[0].range.start.character; item.textEdit.newText = - buffer_line.substr(overwrite_begin.character, overwrite_len - cur_edit_len) + + buffer_line.substr(overwrite_begin.character, + overwrite_len - cur_edit_len) + edits[0].newText + item.textEdit.newText; edits.erase(edits.begin()); } else { @@ -304,7 +307,8 @@ CompletionItemKind GetCompletionKind(CodeCompletionContext::Kind K, return CompletionItemKind::Field; default: - LOG_S(WARNING) << "Unhandled " << int(D->getKind()); + if (auto v = Verbosity::WARNING; LogRequire(v)) + Log(v, "Unhandled ", int(D->getKind())); return CompletionItemKind::Text; } break; diff --git a/src/messages/workspace.cc b/src/messages/workspace.cc index 60ff01ae..785ddaa0 100644 --- a/src/messages/workspace.cc +++ b/src/messages/workspace.cc @@ -13,13 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "sema_manager.hh" #include "fuzzy_match.hh" #include "log.hh" #include "message_handler.hh" #include "pipeline.hh" #include "project.hh" #include "query.hh" +#include "sema_manager.hh" #include #include @@ -30,6 +30,7 @@ limitations under the License. #include #include using namespace llvm; +using namespace ccls::log; namespace ccls { REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName); @@ -81,7 +82,8 @@ void MessageHandler::workspace_didChangeWorkspaceFolders( for (const WorkspaceFolder &wf : param.event.removed) { std::string root = wf.uri.GetPath(); EnsureEndsInSlash(root); - LOG_S(INFO) << "delete workspace folder " << wf.name << ": " << root; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "delete workspace folder ", wf.name, ": ", root); auto it = llvm::find_if(g_config->workspaceFolders, [&](auto &folder) { return folder.first == root; }); if (it != g_config->workspaceFolders.end()) { @@ -100,8 +102,9 @@ void MessageHandler::workspace_didChangeWorkspaceFolders( std::string real = RealPath(folder) + '/'; if (folder == real) real.clear(); - LOG_S(INFO) << "add workspace folder " << wf.name << ": " - << (real.empty() ? folder : folder + " -> " + real); + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "add workspace folder ", wf.name, ": ", + (real.empty() ? folder : folder + " -> " + real)); workspaceFolders.emplace_back(); auto it = workspaceFolders.end() - 1; for (; it != workspaceFolders.begin() && folder < it[-1].first; --it) diff --git a/src/pipeline.cc b/src/pipeline.cc index f2b75524..5a5d6611 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -41,6 +41,7 @@ limitations under the License. #include #endif using namespace llvm; +using namespace ccls::log; namespace chrono = std::chrono; namespace ccls { @@ -118,8 +119,9 @@ bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path, { std::lock_guard lock(vfs->mutex); if (prev->mtime < vfs->state[path].timestamp) { - LOG_V(1) << "timestamp changed for " << path - << (from ? " (via " + *from + ")" : std::string()); + if (auto v = Verbosity::DEBUG; LogRequire(v)) + Log(v, "timestamp changed for ", path, + (from ? " (via " + *from + ")" : std::string())); return true; } } @@ -131,8 +133,9 @@ bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path, if (strcmp(prev->args[i], args[i]) && sys::path::stem(args[i]) != stem) changed = true; if (changed) - LOG_V(1) << "args changed for " << path - << (from ? " (via " + *from + ")" : std::string()); + if (auto v = Verbosity::DEBUG; LogRequire(v)) + Log(v, "args changed for ", path, + (from ? " (via " + *from + ")" : std::string())); return changed; }; @@ -215,7 +218,8 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, } if (!matcher.Matches(request.path)) { - LOG_IF_S(INFO, loud) << "skip " << request.path; + if (auto v = Verbosity::INFO; LogIf(v, loud)) + Log(v, "skip ", request.path); return false; } @@ -264,21 +268,23 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, std::unique_lock lock(GetFileMutex(path_to_index)); prev = RawCacheLoad(path_to_index); if (!prev || CacheInvalid(vfs, prev.get(), path_to_index, entry.args, - std::nullopt)) + std::nullopt)) break; if (track) for (const auto &dep : prev->dependencies) { if (auto mtime1 = LastWriteTime(dep.first.val().str())) { if (dep.second < *mtime1) { reparse = 2; - LOG_V(1) << "timestamp changed for " << path_to_index << " via " - << dep.first.val().str(); + if (auto v = Verbosity::DEBUG; LogRequire(v)) + Log(v, "timestamp changed for ", path_to_index, " via ", + dep.first.val().str()); break; } } else { reparse = 2; - LOG_V(1) << "timestamp changed for " << path_to_index << " via " - << dep.first.val().str(); + if (auto v = Verbosity::DEBUG; LogRequire(v)) + Log(v, "timestamp changed for ", path_to_index, " via ", + dep.first.val().str()); break; } } @@ -289,11 +295,12 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, if (vfs->Loaded(path_to_index)) return true; - LOG_S(INFO) << "load cache for " << path_to_index; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "load cache for ", path_to_index); auto dependencies = prev->dependencies; IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get()); on_indexed->PushBack(std::move(update), - request.mode != IndexMode::NonInteractive); + request.mode != IndexMode::NonInteractive); { std::lock_guard lock1(vfs->mutex); vfs->state[path_to_index].loaded++; @@ -318,7 +325,7 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, } IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get()); on_indexed->PushBack(std::move(update), - request.mode != IndexMode::NonInteractive); + request.mode != IndexMode::NonInteractive); if (entry.id >= 0) { std::lock_guard lock2(project->mtx); project->root2folder[entry.root].path2entry_index[path] = entry.id; @@ -329,12 +336,13 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, if (loud) { std::string line; - if (LOG_V_ENABLED(1)) { + if (LogRequire(Verbosity::DEBUG)) { line = "\n "; for (auto &arg : entry.args) (line += ' ') += arg; } - LOG_S(INFO) << (deleted ? "delete " : "parse ") << path_to_index << line; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, (deleted ? "delete " : "parse "), path_to_index, line); } std::vector> indexes; @@ -367,13 +375,13 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, for (std::unique_ptr &curr : indexes) { std::string path = curr->path; if (!matcher.Matches(path)) { - LOG_IF_S(INFO, loud) << "skip index for " << path; + if (auto v = Verbosity::INFO; LogIf(Verbosity::INFO, loud)) + Log(v, "skip index for ", path); continue; } - if (!deleted) - LOG_IF_S(INFO, loud) << "store index for " << path - << " (delta: " << !!prev << ")"; + if (auto v = Verbosity::INFO; LogIf(v, loud) && !deleted) + Log(v, "store index for ", path, " (delta: ", !!prev, ")"); { std::lock_guard lock(GetFileMutex(path)); int loaded = vfs->Loaded(path), retain = g_config->cache.retainInMemory; @@ -384,7 +392,7 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, if (retain > 0 && retain <= loaded + 1) { std::lock_guard lock(g_index_mutex); auto it = g_index.insert_or_assign( - path, InMemoryIndexFile{curr->file_contents, *curr}); + path, InMemoryIndexFile{curr->file_contents, *curr}); std::string().swap(it.first->second.index.file_contents); } if (g_config->cache.directory.size()) { @@ -468,8 +476,9 @@ void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project, void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) { if (update->refresh) { - LOG_S(INFO) - << "loaded project. Refresh semantic highlight for all working file."; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, + "loaded project. Refresh semantic highlight for all working file."); std::lock_guard lock(wfiles->mutex); for (auto &[f, wf] : wfiles->files) { std::string path = LowerPathIfInsensitive(f); @@ -545,10 +554,12 @@ void LaunchStdin() { std::string method; ReflectMember(reader, "id", id); ReflectMember(reader, "method", method); - if (id.Valid()) - LOG_V(2) << "receive RequestMessage: " << id.value << " " << method; - else - LOG_V(2) << "receive NotificationMessage " << method; + if (auto v = Verbosity::VERBOSE; LogRequire(v)) { + if (id.Valid()) + Log(v, "receive RequestMessage: ", id.value, " ", method); + else + Log(v, "receive NotificationMessage ", method); + } if (method.empty()) continue; received_exit = method == "exit"; @@ -574,7 +585,8 @@ void LaunchStdin() { chrono::steady_clock::now()}); } ThreadLeave(); - }).detach(); + }) + .detach(); } void LaunchStdout() { @@ -592,7 +604,8 @@ void LaunchStdout() { break; } ThreadLeave(); - }).detach(); + }) + .detach(); } void MainLoop() { @@ -702,9 +715,9 @@ void Standalone(const std::string &root) { Project project; WorkingFiles wfiles; VFS vfs; - SemaManager manager( - nullptr, nullptr, [&](std::string, std::vector) {}, - [](RequestId id) {}); + SemaManager manager(nullptr, nullptr, + [&](std::string, std::vector) {}, + [](RequestId id) {}); IncludeComplete complete(&project); MessageHandler handler; @@ -781,7 +794,8 @@ void NotifyOrRequest(const char *method, bool request, JsonWriter writer(&w); fn(writer); w.EndObject(); - LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ") << method; + if (auto v = Verbosity::VERBOSE; LogRequire(v)) + Log(v, (request ? "RequestMessage: " : "NotificationMessage: "), method); for_stdout->PushBack(output.GetString()); } @@ -809,8 +823,8 @@ static void Reply(RequestId id, const char *key, JsonWriter writer(&w); fn(writer); w.EndObject(); - if (id.Valid()) - LOG_V(2) << "respond to RequestMessage: " << id.value; + if (auto v = Verbosity::VERBOSE; LogRequire(v) && id.Valid()) + Log(v, "respond to RequestMessage: ", id.value); for_stdout->PushBack(output.GetString()); } diff --git a/src/project.cc b/src/project.cc index 25bfa34c..8d61a0a0 100644 --- a/src/project.cc +++ b/src/project.cc @@ -37,9 +37,9 @@ limitations under the License. #include #ifdef _WIN32 -# include +#include #else -# include +#include #endif #include @@ -49,6 +49,7 @@ limitations under the License. using namespace clang; using namespace llvm; +using namespace ccls::log; namespace ccls { std::pair lookupExtension(std::string_view filename) { @@ -91,13 +92,17 @@ struct ProjectProcessor { exclude_args.insert(arg); else if (Expected glob_or_err = GlobPattern::create(arg)) exclude_globs.push_back(std::move(*glob_or_err)); - else - LOG_S(WARNING) << toString(glob_or_err.takeError()); + else { + if (auto v = Verbosity::WARNING; LogRequire(v)) + Log(v, toString(glob_or_err.takeError())); + } } bool ExcludesArg(StringRef arg) { + // clang-format off return exclude_args.count(arg) || any_of(exclude_globs, [&](const GlobPattern &glob) { return glob.match(arg); }); + // clang-format on } // Expand %c %cpp ... in .ccls @@ -172,12 +177,12 @@ struct ProjectProcessor { IgnoringDiagConsumer DiagC; IntrusiveRefCntPtr DiagOpts(new DiagnosticOptions()); DiagnosticsEngine Diags( - IntrusiveRefCntPtr(new DiagnosticIDs()), &*DiagOpts, - &DiagC, false); + IntrusiveRefCntPtr(new DiagnosticIDs()), &*DiagOpts, + &DiagC, false); driver::Driver Driver(args[0], llvm::sys::getDefaultTargetTriple(), Diags); auto TargetAndMode = - driver::ToolChain::getTargetAndModeFromProgramName(args[0]); + driver::ToolChain::getTargetAndModeFromProgramName(args[0]); if (!TargetAndMode.TargetPrefix.empty()) { const char *arr[] = {"-target", TargetAndMode.TargetPrefix.c_str()}; args.insert(args.begin() + 1, std::begin(arr), std::end(arr)); @@ -193,7 +198,7 @@ struct ProjectProcessor { auto CI = std::make_unique(); CompilerInvocation::CreateFromArgs(*CI, CCArgs.data(), - CCArgs.data() + CCArgs.size(), Diags); + CCArgs.data() + CCArgs.size(), Diags); CI->getFrontendOpts().DisableFree = false; CI->getCodeGenOpts().DisableFree = false; @@ -265,25 +270,26 @@ void LoadDirectoryListing(ProjectProcessor &proc, const std::string &root, return folder.dot_ccls[root]; }; - GetFilesInFolder(root, true /*recursive*/, true /*add_folder_to_path*/, - [&folder, &files, &Seen](const std::string &path) { - std::pair lang = lookupExtension(path); - if (lang.first != LanguageId::Unknown && !lang.second) { - if (!Seen.count(path)) - files.push_back(path); - } else if (sys::path::filename(path) == ".ccls") { - std::vector args = ReadCompilerArgumentsFromFile(path); - folder.dot_ccls.emplace(sys::path::parent_path(path), - args); - std::string l; - for (size_t i = 0; i < args.size(); i++) { - if (i) - l += ' '; - l += args[i]; - } - LOG_S(INFO) << "use " << path << ": " << l; - } - }); + GetFilesInFolder( + root, true /*recursive*/, true /*add_folder_to_path*/, + [&folder, &files, &Seen](const std::string &path) { + std::pair lang = lookupExtension(path); + if (lang.first != LanguageId::Unknown && !lang.second) { + if (!Seen.count(path)) + files.push_back(path); + } else if (sys::path::filename(path) == ".ccls") { + std::vector args = ReadCompilerArgumentsFromFile(path); + folder.dot_ccls.emplace(sys::path::parent_path(path), args); + std::string l; + for (size_t i = 0; i < args.size(); i++) { + if (i) + l += ' '; + l += args[i]; + } + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "use ", path, ": ", l); + } + }); // If the first line of .ccls is %compile_commands.json, append extra flags. for (auto &e : folder.entries) @@ -367,8 +373,9 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) { std::vector args{g_config->compilationDatabaseCommand, root}; if (sys::ExecuteAndWait(args[0], args, llvm::None, Redir, 0, 0, &err_msg) < 0) { - LOG_S(ERROR) << "failed to execute " << args[0].str() << " " - << args[1].str() << ": " << err_msg; + if (auto v = Verbosity::ERROR; LogRequire(v)) + Log(v, "failed to execute ", args[0].str(), " ", args[1].str(), ": ", + err_msg); return; } } @@ -392,9 +399,11 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) { std::vector result; if (!CDB) { if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(Path)) - LOG_S(ERROR) << "failed to load " << Path.c_str(); + if (auto v = Verbosity::ERROR; LogRequire(v)) + Log(v, "failed to load ", Path.c_str()); } else { - LOG_S(INFO) << "loaded " << Path.c_str(); + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "loaded ", Path.c_str()); for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) { static bool once; Project::Entry entry; @@ -458,7 +467,8 @@ void Project::Load(const std::string &root) { LoadDirectory(root, folder); for (auto &[path, kind] : folder.search_dir2kind) - LOG_S(INFO) << "search directory: " << path << ' ' << " \"< "[kind]; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "search directory: ", path, ' ', " \"< "[kind]); // Setup project entries. folder.path2entry_index.reserve(folder.entries.size()); @@ -579,8 +589,9 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) { : IndexMode::NonInteractive, false, id); } else { - LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason - << "; skip " << entry.filename; + if (auto v = Verbosity::DEBUG; LogRequire(v)) + Log(v, "[", i, "/", folder.entries.size(), "]: ", reason, "; skip ", + entry.filename); } i++; } diff --git a/src/sema_manager.cc b/src/sema_manager.cc index d78b5e06..87a4def1 100644 --- a/src/sema_manager.cc +++ b/src/sema_manager.cc @@ -30,6 +30,7 @@ limitations under the License. #include using namespace clang; using namespace llvm; +using namespace ccls::log; #include #include @@ -722,12 +723,13 @@ SemaManager::EnsureSession(const std::string &path, bool *created) { session = std::make_shared( project_->FindEntry(path, false, false), wfiles, PCH); std::string line; - if (LOG_V_ENABLED(1)) { + if (LogRequire(Verbosity::DEBUG)) { line = "\n "; for (auto &arg : session->file.args) (line += ' ') += arg; } - LOG_S(INFO) << "create session for " << path << line; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "create session for ", path, line); sessions.Insert(path, session); if (created) *created = true; @@ -736,7 +738,8 @@ SemaManager::EnsureSession(const std::string &path, bool *created) { } void SemaManager::Clear() { - LOG_S(INFO) << "clear all sessions"; + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "clear all sessions"); std::lock_guard lock(mutex); sessions.Clear(); } diff --git a/src/serializer.cc b/src/serializer.cc index 0dec4710..3fd2ca59 100644 --- a/src/serializer.cc +++ b/src/serializer.cc @@ -30,6 +30,7 @@ limitations under the License. #include using namespace llvm; +using namespace ccls::log; bool gTestOutputMode = false; @@ -426,9 +427,7 @@ CachedHashStringRef InternH(StringRef S) { return *R.first; } -const char *Intern(StringRef S) { - return InternH(S).val().data(); -} +const char *Intern(StringRef S) { return InternH(S).val().data(); } std::string Serialize(SerializeFormat format, IndexFile &file) { switch (format) { @@ -485,7 +484,8 @@ Deserialize(SerializeFormat format, const std::string &path, file = std::make_unique(path, file_content); ReflectFile(reader, *file); } catch (std::invalid_argument &e) { - LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what(); + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "failed to deserialize '", path, "': ", e.what()); return nullptr; } break; @@ -510,8 +510,9 @@ Deserialize(SerializeFormat format, const std::string &path, try { ReflectFile(json_reader, *file); } catch (std::invalid_argument &e) { - LOG_S(INFO) << "'" << path << "': failed to deserialize " - << json_reader.GetPath() << "." << e.what(); + if (auto v = Verbosity::INFO; LogRequire(v)) + Log(v, "'", path, "': failed to deserialize ", json_reader.GetPath(), + ".", e.what()); return nullptr; } break; diff --git a/src/utils.cc b/src/utils.cc index 6ebb4a6e..f57da7f6 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -36,6 +36,7 @@ limitations under the License. #include using namespace llvm; +using namespace ccls::log; namespace ccls { struct Matcher::Impl { @@ -182,7 +183,8 @@ void WriteToFile(const std::string &filename, const std::string &content) { FILE *f = fopen(filename.c_str(), "wb"); if (!f || (content.size() && fwrite(content.c_str(), content.size(), 1, f) != 1)) { - LOG_S(ERROR) << "failed to write to " << filename << ' ' << strerror(errno); + if (auto v = Verbosity::ERROR; LogRequire(v)) + Log(v, "failed to write to ", filename, ' ', strerror(errno)); return; } fclose(f); @@ -206,4 +208,4 @@ int ReverseSubseqMatch(std::string_view pat, std::string_view text, } std::string GetDefaultResourceDirectory() { return DEFAULT_RESOURCE_DIRECTORY; } -} +} // namespace ccls diff --git a/src/working_files.cc b/src/working_files.cc index 6ba8a963..6014e915 100644 --- a/src/working_files.cc +++ b/src/working_files.cc @@ -28,6 +28,7 @@ namespace chrono = std::chrono; using namespace clang; using namespace llvm; +using namespace ccls::log; namespace ccls { namespace { @@ -329,8 +330,9 @@ std::optional WorkingFile::GetBufferPosFromIndexPos(int line, int *column, if (line == (int)index_lines.size() && !*column) return buffer_content.size(); if (line < 0 || line >= (int)index_lines.size()) { - LOG_S(WARNING) << "bad index_line (got " << line << ", expected [0, " - << index_lines.size() << ")) in " << filename; + if (auto v = Verbosity::WARNING; LogRequire(v)) + Log(v, "bad index_line (got ", line, ", expected [0, ", + index_lines.size(), ")) in ", filename); return std::nullopt; } @@ -406,7 +408,8 @@ void WorkingFiles::OnChange(const TextDocumentDidChangeParam &change) { std::string path = change.textDocument.uri.GetPath(); WorkingFile *file = GetFileUnlocked(path); if (!file) { - LOG_S(WARNING) << "Could not change " << path << " because it was not open"; + if (auto v = Verbosity::WARNING; LogRequire(v)) + Log(v,"Could not change ", path, " because it was not open"); return; } @@ -482,4 +485,4 @@ std::string_view LexIdentifierAroundPos(Position position, return content.substr(start, end - start); } -} +} // namespace ccls