From 202ab60edeed2afafb3a008222275d0595a8a63d Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Tue, 9 May 2017 23:13:13 -0700 Subject: [PATCH] Added diagnostics. --- src/command_line.cc | 8 ++ src/indexer.cc | 151 +++++++++++++++++++++++--------------- src/indexer.h | 7 +- src/ipc.cc | 2 + src/ipc.h | 1 + src/language_server_api.h | 58 +++++++++++++++ 6 files changed, 165 insertions(+), 62 deletions(-) diff --git a/src/command_line.cc b/src/command_line.cc index ea657840..bd913242 100644 --- a/src/command_line.cc +++ b/src/command_line.cc @@ -990,6 +990,14 @@ void ParseFile(IndexerConfig* config, for (std::unique_ptr& new_index : indexes) { std::cerr << "Got index for " << new_index->path << std::endl; + // Publish diagnostics. + if (!new_index->diagnostics.empty()) { + Out_TextDocumentPublishDiagnostics diag; + diag.params.uri = lsDocumentUri::FromPath(new_index->path); + diag.params.diagnostics = new_index->diagnostics; + IpcManager::instance()->SendOutMessageToClient(IpcId::TextDocumentPublishDiagnostics, diag); + } + // Load the cached index. std::unique_ptr cached_index; if (cache_for_args && new_index->path == cache_for_args->path) diff --git a/src/indexer.cc b/src/indexer.cc index bf74e4e7..14cadbfd 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -20,6 +20,31 @@ void AddFuncRef(std::vector* result, IndexFuncRef ref) { result->push_back(ref); } + +Range Resolve(const CXSourceRange& range) { + CXSourceLocation start = clang_getRangeStart(range); + CXSourceLocation end = clang_getRangeEnd(range); + + unsigned int start_line, start_column; + clang_getSpellingLocation(start, nullptr, &start_line, &start_column, nullptr); + unsigned int end_line, end_column; + clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr); + + return Range( + Position(start_line, start_column) /*start*/, + Position(end_line, end_column) /*end*/); +} + +Range ResolveSpelling(const CXCursor& cx_cursor) { + CXSourceRange cx_range = clang_Cursor_getSpellingNameRange(cx_cursor, 0, 0); + return Resolve(cx_range); +} + +Range ResolveExtent(const CXCursor& cx_cursor) { + CXSourceRange cx_range = clang_getCursorExtent(cx_cursor); + return Resolve(cx_range); +} + } // namespace @@ -120,30 +145,6 @@ void UniqueAdd(std::vector& uses, Range loc) { IdCache::IdCache(const std::string& primary_file) : primary_file(primary_file) {} -Range IdCache::Resolve(const CXSourceRange& range) { - CXSourceLocation start = clang_getRangeStart(range); - CXSourceLocation end = clang_getRangeEnd(range); - - unsigned int start_line, start_column; - clang_getSpellingLocation(start, nullptr, &start_line, &start_column, nullptr); - unsigned int end_line, end_column; - clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr); - - return Range( - Position(start_line, start_column) /*start*/, - Position(end_line, end_column) /*end*/); -} - -Range IdCache::ResolveSpelling(const CXCursor& cx_cursor) { - CXSourceRange cx_range = clang_Cursor_getSpellingNameRange(cx_cursor, 0, 0); - return Resolve(cx_range); -} - -Range IdCache::ResolveExtent(const CXCursor& cx_cursor) { - CXSourceRange cx_range = clang_getCursorExtent(cx_cursor); - return Resolve(cx_range); -} - template bool Contains(const std::vector& vec, const T& element) { for (const T& entry : vec) { @@ -220,25 +221,59 @@ void diagnostic(CXClientData client_data, for (unsigned i = 0; i < clang_getNumDiagnosticsInSet(diagnostics); ++i) { CXDiagnostic diagnostic = clang_getDiagnosticInSet(diagnostics, i); - std::string spelling = - clang::ToString(clang_getDiagnosticSpelling(diagnostic)); + // Skip diagnostics in system headers. + CXSourceLocation diag_loc = clang_getDiagnosticLocation(diagnostic); + if (clang_Location_isInSystemHeader(diag_loc)) + continue; - // Fetch location + // Get db so we can attribute diagnostic to the right indexed file. CXFile file; unsigned int line, column; - CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); - if (!clang_Location_isInSystemHeader(location)) { - clang_getSpellingLocation(location, &file, &line, &column, nullptr); + clang_getSpellingLocation(diag_loc, &file, &line, &column, nullptr); + bool is_first_time_visiting_file = false; + IndexedFile* db = param->file_consumer->TryConsumeFile(file, &is_first_time_visiting_file); + if (!db) + continue; + if (is_first_time_visiting_file && param->primary_file) + param->primary_file->dependencies.push_back(db->path); - // Fetch path, print. - if (file != nullptr) { - std::string path = clang::ToString(clang_getFileName(file)); - std::cerr << NormalizePath(path) << ':'; - } - std::cerr << line << ':' << column << ": " << spelling << std::endl; + // Build diagnostic. + + lsDiagnostic ls_diagnostic; + + // TODO: ls_diagnostic.range is lsRange, we have Range. We should only be + // storing Range types when inside the indexer so that index <-> buffer + // remapping logic is applied. + ls_diagnostic.range = lsRange(lsPosition(line - 1, column), lsPosition(line - 1, column)); + + ls_diagnostic.message = clang::ToString(clang_getDiagnosticSpelling(diagnostic)); + + // Append the flag that enables this diagnostic, ie, [-Wswitch] + std::string enabling_flag = clang::ToString(clang_getDiagnosticOption(diagnostic, nullptr)); + if (!enabling_flag.empty()) + ls_diagnostic.message += " [" + enabling_flag + "]"; + + ls_diagnostic.code = clang_getDiagnosticCategory(diagnostic); + + switch (clang_getDiagnosticSeverity(diagnostic)) { + case CXDiagnostic_Ignored: + case CXDiagnostic_Note: + ls_diagnostic.severity = lsDiagnosticSeverity::Information; + break; + case CXDiagnostic_Warning: + ls_diagnostic.severity = lsDiagnosticSeverity::Warning; + break; + case CXDiagnostic_Error: + case CXDiagnostic_Fatal: + ls_diagnostic.severity = lsDiagnosticSeverity::Error; + break; } + // TODO: integrate FixIts (ie, textDocument/codeAction) + clang_disposeDiagnostic(diagnostic); + + db->diagnostics.push_back(ls_diagnostic); } } @@ -374,7 +409,7 @@ void VisitDeclForTypeUsageVisitorHandler(clang::Cursor cursor, IndexedTypeDef* ref_type_def = db->Resolve(ref_type_id); // TODO: Should we even be visiting this if the file is not from the main // def? Try adding assert on |loc| later. - Range loc = db->id_cache.ResolveSpelling(cursor.cx_cursor); + Range loc = ResolveSpelling(cursor.cx_cursor); UniqueAdd(ref_type_def->uses, loc); } @@ -611,7 +646,7 @@ clang::VisiterResult AddDeclInitializerUsagesVisitor(clang::Cursor cursor, if (ref_usr == "") break; - Range loc = db->id_cache.ResolveSpelling(cursor.cx_cursor); + Range loc = ResolveSpelling(cursor.cx_cursor); // std::cerr << "Adding usage to id=" << ref_id.id << " usr=" << ref_usr // << " at " << loc.ToString() << std::endl; IndexVarId ref_id = db->ToVarId(ref_usr); @@ -726,7 +761,7 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { case CXIdxEntity_Field: case CXIdxEntity_Variable: case CXIdxEntity_CXXStaticVariable: { - Range decl_loc_spelling = db->id_cache.ResolveSpelling(decl->cursor); + Range decl_loc_spelling = ResolveSpelling(decl->cursor); clang::Cursor decl_cursor = decl->cursor; @@ -751,11 +786,11 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { //} if (decl->isDefinition) { - var_def->def.definition_spelling = db->id_cache.ResolveSpelling(decl->cursor); - var_def->def.definition_extent = db->id_cache.ResolveExtent(decl->cursor);; + var_def->def.definition_spelling = ResolveSpelling(decl->cursor); + var_def->def.definition_extent = ResolveExtent(decl->cursor);; } else { - var_def->def.declaration = db->id_cache.ResolveSpelling(decl->cursor); + var_def->def.declaration = ResolveSpelling(decl->cursor); } UniqueAdd(var_def->uses, decl_loc_spelling); @@ -806,7 +841,7 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { case CXIdxEntity_CXXInstanceMethod: case CXIdxEntity_CXXStaticMethod: case CXIdxEntity_CXXConversionFunction: { - Range decl_loc_spelling = db->id_cache.ResolveSpelling(decl->cursor); + Range decl_loc_spelling = ResolveSpelling(decl->cursor); clang::Cursor decl_cursor = decl->cursor; clang::Cursor resolved = @@ -824,13 +859,13 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { // hacking the 'declarations' field by // adding a definition when we really don't have one. if (decl->isDefinition && !func_def->def.definition_extent.has_value()) { - func_def->def.definition_spelling = db->id_cache.ResolveSpelling(decl->cursor); - func_def->def.definition_extent = db->id_cache.ResolveExtent(decl->cursor); + func_def->def.definition_spelling = ResolveSpelling(decl->cursor); + func_def->def.definition_extent = ResolveExtent(decl->cursor); RemoveItem(func_def->declarations, *func_def->def.definition_spelling); } else { - Range decl_spelling = db->id_cache.ResolveSpelling(decl->cursor); + Range decl_spelling = ResolveSpelling(decl->cursor); // Only add the declaration if it's not already a definition. if (!func_def->def.definition_spelling || *func_def->def.definition_spelling != decl_spelling) UniqueAdd(func_def->declarations, decl_spelling); @@ -962,7 +997,7 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { case CXIdxEntity_Typedef: case CXIdxEntity_CXXTypeAlias: { - Range decl_loc_spelling = db->id_cache.ResolveSpelling(decl->cursor); + Range decl_loc_spelling = ResolveSpelling(decl->cursor); // Note we want to fetch the first TypeRef. Running // ResolveCursorType(decl->cursor) would return @@ -981,8 +1016,8 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { type_def->def.detailed_name = ns->QualifiedName(decl->semanticContainer, type_def->def.short_name); - type_def->def.definition_spelling = db->id_cache.ResolveSpelling(decl->cursor); - type_def->def.definition_extent = db->id_cache.ResolveExtent(decl->cursor); + type_def->def.definition_spelling = ResolveSpelling(decl->cursor); + type_def->def.definition_extent = ResolveExtent(decl->cursor); UniqueAdd(type_def->uses, decl_loc_spelling); break; } @@ -991,7 +1026,7 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { case CXIdxEntity_Union: case CXIdxEntity_Struct: case CXIdxEntity_CXXClass: { - Range decl_loc_spelling = db->id_cache.ResolveSpelling(decl->cursor); + Range decl_loc_spelling = ResolveSpelling(decl->cursor); IndexTypeId type_id = db->ToTypeId(decl->entityInfo->USR); IndexedTypeDef* type_def = db->Resolve(type_id); @@ -1018,8 +1053,8 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { // } assert(decl->isDefinition); - type_def->def.definition_spelling = db->id_cache.ResolveSpelling(decl->cursor); - type_def->def.definition_extent = db->id_cache.ResolveExtent(decl->cursor); + type_def->def.definition_spelling = ResolveSpelling(decl->cursor); + type_def->def.definition_extent = ResolveExtent(decl->cursor); UniqueAdd(type_def->uses, decl_loc_spelling); // type_def->alias_of @@ -1056,7 +1091,7 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { std::cerr << "!! Unhandled indexDeclaration: " << clang::Cursor(decl->cursor).ToString() << " at " - << db->id_cache.ResolveSpelling(decl->cursor).start.ToString() + << ResolveSpelling(decl->cursor).start.ToString() << std::endl; std::cerr << " entityInfo->kind = " << decl->entityInfo->kind << std::endl; @@ -1199,7 +1234,7 @@ void indexEntityReference(CXClientData client_data, case CXIdxEntity_CXXStaticVariable: case CXIdxEntity_Variable: case CXIdxEntity_Field: { - Range loc_spelling = db->id_cache.ResolveSpelling(ref->cursor); + Range loc_spelling = ResolveSpelling(ref->cursor); clang::Cursor referenced = ref->referencedEntity->cursor; referenced = referenced.template_specialization_to_template_definition(); @@ -1226,7 +1261,7 @@ void indexEntityReference(CXClientData client_data, // } // TODO: search full history? - Range loc_spelling = db->id_cache.ResolveSpelling(ref->cursor); + Range loc_spelling = ResolveSpelling(ref->cursor); // Note: be careful, calling db->ToFuncId invalidates the FuncDef* ptrs. IndexFuncId called_id = db->ToFuncId(ref->referencedEntity->USR); @@ -1251,7 +1286,7 @@ void indexEntityReference(CXClientData client_data, case CXIdxEntity_Union: case CXIdxEntity_Struct: case CXIdxEntity_CXXClass: { - Range loc_spelling = db->id_cache.ResolveSpelling(ref->cursor); + Range loc_spelling = ResolveSpelling(ref->cursor); clang::Cursor referenced = ref->referencedEntity->cursor; referenced = referenced.template_specialization_to_template_definition(); @@ -1283,7 +1318,7 @@ void indexEntityReference(CXClientData client_data, std::cerr << "!! Unhandled indexEntityReference: " << cursor.ToString() << " at " - << db->id_cache.ResolveSpelling(ref->cursor).start.ToString() + << ResolveSpelling(ref->cursor).start.ToString() << std::endl; std::cerr << " ref->referencedEntity->kind = " << ref->referencedEntity->kind << std::endl; @@ -1292,7 +1327,7 @@ void indexEntityReference(CXClientData client_data, << ref->parentEntity->kind << std::endl; std::cerr << " ref->loc = " - << db->id_cache.ResolveSpelling(ref->cursor).start.ToString() + << ResolveSpelling(ref->cursor).start.ToString() << std::endl; std::cerr << " ref->kind = " << ref->kind << std::endl; if (ref->parentEntity) diff --git a/src/indexer.h b/src/indexer.h index 97c19e23..acd6f4e0 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -453,10 +453,6 @@ struct IdCache { std::unordered_map var_id_to_usr; IdCache(const std::string& primary_file); - - Range Resolve(const CXSourceRange& range); - Range ResolveSpelling(const CXCursor& cx_cursor); - Range ResolveExtent(const CXCursor& cx_cursor); }; struct IndexedFile { @@ -478,6 +474,9 @@ struct IndexedFile { // The content of |path| when it was indexed. //std::string content; + // Diagnostics found when indexing the file. This is not saved. + NonElidedVector diagnostics; + std::vector dependencies; std::vector types; std::vector funcs; diff --git a/src/ipc.cc b/src/ipc.cc index 6027fd95..040f2ebd 100644 --- a/src/ipc.cc +++ b/src/ipc.cc @@ -20,6 +20,8 @@ const char* IpcIdToString(IpcId id) { return "textDocument/didClose"; case IpcId::TextDocumentDidSave: return "textDocument/didSave"; + case IpcId::TextDocumentPublishDiagnostics: + return "textDocument/publishDiagnostics"; case IpcId::TextDocumentRename: return "textDocument/rename"; case IpcId::TextDocumentCompletion: diff --git a/src/ipc.h b/src/ipc.h index 29ede301..8a0ce65b 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -15,6 +15,7 @@ enum class IpcId : int { TextDocumentDidChange, TextDocumentDidClose, TextDocumentDidSave, + TextDocumentPublishDiagnostics, TextDocumentRename, TextDocumentCompletion, TextDocumentDefinition, diff --git a/src/language_server_api.h b/src/language_server_api.h index a79ac7a0..25468f13 100644 --- a/src/language_server_api.h +++ b/src/language_server_api.h @@ -555,6 +555,39 @@ struct lsDocumentHighlight { }; MAKE_REFLECT_STRUCT(lsDocumentHighlight, range, kind); +enum class lsDiagnosticSeverity { + // Reports an error. + Error = 1, + // Reports a warning. + Warning = 2, + // Reports an information. + Information = 3, + // Reports a hint. + Hint = 4 +}; +MAKE_REFLECT_TYPE_PROXY(lsDiagnosticSeverity, int); + +struct lsDiagnostic { + // The range at which the message applies. + lsRange range; + + // The diagnostic's severity. Can be omitted. If omitted it is up to the + // client to interpret diagnostics as error, warning, info or hint. + optional severity; + + // The diagnostic's code. Can be omitted. + int code = 0; + + // A human-readable string describing the source of this + // diagnostic, e.g. 'typescript' or 'super lint'. + std::string source = "cquery"; + + // The diagnostic's message. + std::string message; +}; +MAKE_REFLECT_STRUCT(lsDiagnostic, range, severity, source, message); + + // TODO: DocumentFilter // TODO: DocumentSelector @@ -1162,6 +1195,31 @@ MAKE_REFLECT_STRUCT(Ipc_TextDocumentDidSave, params); +// Diagnostics +struct Out_TextDocumentPublishDiagnostics : public lsOutMessage { + struct Params { + // The URI for which diagnostic information is reported. + lsDocumentUri uri; + + // An array of diagnostic information items. + NonElidedVector diagnostics; + }; + + Params params; +}; +template +void Reflect(TVisitor& visitor, Out_TextDocumentPublishDiagnostics& value) { + std::string method = "textDocument/publishDiagnostics"; + REFLECT_MEMBER_START(); + REFLECT_MEMBER(jsonrpc); + REFLECT_MEMBER2("method", method); + REFLECT_MEMBER(params); + REFLECT_MEMBER_END(); +} +MAKE_REFLECT_STRUCT(Out_TextDocumentPublishDiagnostics::Params, uri, diagnostics); + + + // Rename struct Ipc_TextDocumentRename : public IpcMessage { struct Params {