Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -466,6 +466,7 @@ {"range", Diag.range}, {"severity", Diag.severity}, {"message", Diag.message}, + {"relatedInformation", json::Array(Diag.relatedInformation)} }); auto &FixItsForDiagnostic = LocalFixIts[Diag]; Index: clangd/Diagnostics.h =================================================================== --- clangd/Diagnostics.h +++ clangd/Diagnostics.h @@ -12,11 +12,13 @@ #include "Path.h" #include "Protocol.h" +#include "URI.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LangOptions.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Optional.h" #include #include @@ -29,6 +31,7 @@ // Intended to be used only in error messages. // May be relative, absolute or even artifically constructed. std::string File; + llvm::Optional AbsPath; clangd::Range Range; DiagnosticsEngine::Level Severity = DiagnosticsEngine::Note; // Since File is only descriptive, we store a separate flag to distinguish Index: clangd/Diagnostics.cpp =================================================================== --- clangd/Diagnostics.cpp +++ clangd/Diagnostics.cpp @@ -231,19 +231,19 @@ return Res; }; - { - clangd::Diagnostic Main = FillBasicFields(D); - Main.message = mainMessage(D); - OutFn(std::move(Main), D.Fixes); - } - + clangd::Diagnostic Main = FillBasicFields(D); + Main.message = D.Message; + Main.relatedInformation.reserve(D.Notes.size()); for (auto &Note : D.Notes) { - if (!Note.InsideMainFile) - continue; - clangd::Diagnostic Res = FillBasicFields(Note); - Res.message = noteMessage(D, Note); - OutFn(std::move(Res), llvm::ArrayRef()); + log("Note {0}", Note.Message); + DiagnosticRelatedInformation NoteInfo; + NoteInfo.location = clangd::Location{ + clangd::URIForFile(), Note.Range}; + NoteInfo.message = Note.Message; + + Main.relatedInformation.push_back(std::move(NoteInfo)); } + OutFn(std::move(Main), D.Fixes); } int getSeverity(DiagnosticsEngine::Level L) { @@ -285,6 +285,7 @@ } bool InsideMainFile = isInsideMainFile(Info); + const SourceManager &SrcMgr = Info.getSourceManager(); auto FillDiagBase = [&](DiagBase &D) { D.Range = diagnosticRange(Info, *LangOpts); @@ -292,7 +293,12 @@ Info.FormatDiagnostic(Message); D.Message = Message.str(); D.InsideMainFile = InsideMainFile; - D.File = Info.getSourceManager().getFilename(Info.getLocation()); + + D.File = SrcMgr.getFilename(Info.getLocation()); + const FileID FID = + SrcMgr.getFileID(Info.getLocation()); + const FileEntry *FE = SrcMgr.getFileEntryForID(FID); + D.AbsPath = getAbsoluteFilePath(FE, SrcMgr); D.Severity = DiagLevel; return D; }; Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -155,6 +155,7 @@ return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range); } }; +bool fromJSON(const llvm::json::Value&, Location &); llvm::json::Value toJSON(const Location &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Location &); @@ -304,11 +305,20 @@ }; bool fromJSON(const llvm::json::Value &, WorkspaceClientCapabilities &); +struct PublishDiagnosticsClientCapabilities { + /// Whether the clients accepts diagnostics with related information. + bool relatedInformation = false; +}; +bool fromJSON(const llvm::json::Value &, PublishDiagnosticsClientCapabilities &); + // FIXME: most of the capabilities are missing from this struct. Only the ones // used by clangd are currently there. struct TextDocumentClientCapabilities { /// Capabilities specific to the `textDocument/completion` CompletionClientCapabilities completion; + + /// Capabilities specific to `textDocument/publishDiagnostics`. + PublishDiagnosticsClientCapabilities publishDiagnostics; }; bool fromJSON(const llvm::json::Value &, TextDocumentClientCapabilities &); @@ -485,6 +495,15 @@ }; bool fromJSON(const llvm::json::Value &, DocumentSymbolParams &); +struct DiagnosticRelatedInformation { + /// The location of this related diagnostic information. + Location location; + /// The message of this related diagnostic information. + std::string message; +}; +bool fromJSON(const llvm::json::Value &, DiagnosticRelatedInformation &); +llvm::json::Value toJSON(const DiagnosticRelatedInformation &); + struct Diagnostic { /// The range at which the message applies. Range range; @@ -504,6 +523,10 @@ /// The diagnostic's message. std::string message; + + /// An array of related diagnostic information, e.g. when symbol-names within + /// a scope collide all definitions can be marked via this property. + std::vector relatedInformation; }; /// A LSP-specific comparator used to find diagnostic in a container like Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -100,6 +100,11 @@ return OS << R.start << '-' << R.end; } +bool fromJSON(const llvm::json::Value& Params, Location &P) { + json::ObjectMapper O(Params); + return O && O.map("uri", P.uri) && O.map("range", P.range); +} + json::Value toJSON(const Location &P) { return json::Object{ {"uri", P.uri}, @@ -235,11 +240,17 @@ return O && O.map("symbol", R.symbol); } +bool fromJSON(const json::Value &Params, PublishDiagnosticsClientCapabilities &R) { + json::ObjectMapper O(Params); + return O && O.map("symbol", R.relatedInformation); +} + bool fromJSON(const json::Value &Params, TextDocumentClientCapabilities &R) { json::ObjectMapper O(Params); if (!O) return false; O.map("completion", R.completion); + O.map("publishDiagnostics", R.publishDiagnostics); return true; } @@ -349,11 +360,21 @@ return O && O.map("textDocument", R.textDocument); } +bool fromJSON(const llvm::json::Value &Params, DiagnosticRelatedInformation &I) { + json::ObjectMapper O(Params); + return O && O.map("location", I.location) && O.map("message", I.message); +} + +llvm::json::Value toJSON(const DiagnosticRelatedInformation &I) { + return json::Object{{"location", I.location}, {"message", I.message}}; +} + bool fromJSON(const json::Value &Params, Diagnostic &R) { json::ObjectMapper O(Params); if (!O || !O.map("range", R.range) || !O.map("message", R.message)) return false; O.map("severity", R.severity); + O.map("relatedInformation", R.relatedInformation); return true; }