mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 07:35:08 +00:00
Added diagnostics.
This commit is contained in:
parent
1083a10a66
commit
202ab60ede
@ -990,6 +990,14 @@ void ParseFile(IndexerConfig* config,
|
||||
for (std::unique_ptr<IndexedFile>& 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<IndexedFile> cached_index;
|
||||
if (cache_for_args && new_index->path == cache_for_args->path)
|
||||
|
151
src/indexer.cc
151
src/indexer.cc
@ -20,6 +20,31 @@ void AddFuncRef(std::vector<IndexFuncRef>* 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<Range>& 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 <typename T>
|
||||
bool Contains(const std::vector<T>& 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)
|
||||
|
@ -453,10 +453,6 @@ struct IdCache {
|
||||
std::unordered_map<IndexVarId, std::string> 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<lsDiagnostic> diagnostics;
|
||||
|
||||
std::vector<std::string> dependencies;
|
||||
std::vector<IndexedTypeDef> types;
|
||||
std::vector<IndexedFuncDef> funcs;
|
||||
|
@ -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:
|
||||
|
@ -15,6 +15,7 @@ enum class IpcId : int {
|
||||
TextDocumentDidChange,
|
||||
TextDocumentDidClose,
|
||||
TextDocumentDidSave,
|
||||
TextDocumentPublishDiagnostics,
|
||||
TextDocumentRename,
|
||||
TextDocumentCompletion,
|
||||
TextDocumentDefinition,
|
||||
|
@ -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<lsDiagnosticSeverity> 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<Out_TextDocumentPublishDiagnostics> {
|
||||
struct Params {
|
||||
// The URI for which diagnostic information is reported.
|
||||
lsDocumentUri uri;
|
||||
|
||||
// An array of diagnostic information items.
|
||||
NonElidedVector<lsDiagnostic> diagnostics;
|
||||
};
|
||||
|
||||
Params params;
|
||||
};
|
||||
template<typename TVisitor>
|
||||
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<Ipc_TextDocumentRename> {
|
||||
struct Params {
|
||||
|
Loading…
Reference in New Issue
Block a user