diff options
Diffstat (limited to 'clang-tools-extra/clangd/Diagnostics.cpp')
| -rw-r--r-- | clang-tools-extra/clangd/Diagnostics.cpp | 110 |
1 files changed, 66 insertions, 44 deletions
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp index aee19258af2..a734f233c8f 100644 --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/clang-tools-extra/clangd/Diagnostics.cpp @@ -10,6 +10,7 @@ #include "../clang-tidy/ClangTidyDiagnosticConsumer.h" #include "Compiler.h" #include "Logger.h" +#include "Protocol.h" #include "SourceCode.h" #include "clang/Basic/AllDiagnostics.h" #include "clang/Basic/DiagnosticIDs.h" @@ -175,9 +176,7 @@ std::string capitalize(std::string Message) { } /// Returns a message sent to LSP for the main diagnostic in \p D. -/// The message includes all the notes with their corresponding locations. -/// However, notes with fix-its are excluded as those usually only contain a -/// fix-it message and just add noise if included in the message for diagnostic. +/// This message may include notes, if they're not emited in some other way. /// Example output: /// /// no matching function for call to 'foo' @@ -186,29 +185,34 @@ std::string capitalize(std::string Message) { /// /// dir1/dir2/dir3/../../dir4/header.h:12:23 /// note: candidate function not viable: requires 3 arguments -std::string mainMessage(const Diag &D, bool DisplayFixesCount) { +std::string mainMessage(const Diag &D, const ClangdDiagnosticOptions &Opts) { std::string Result; llvm::raw_string_ostream OS(Result); OS << D.Message; - if (DisplayFixesCount && !D.Fixes.empty()) + if (Opts.DisplayFixesCount && !D.Fixes.empty()) OS << " (" << (D.Fixes.size() > 1 ? "fixes" : "fix") << " available)"; - for (auto &Note : D.Notes) { - OS << "\n\n"; - printDiag(OS, Note); - } + // If notes aren't emitted as structured info, add them to the message. + if (!Opts.EmitRelatedLocations) + for (auto &Note : D.Notes) { + OS << "\n\n"; + printDiag(OS, Note); + } OS.flush(); return capitalize(std::move(Result)); } /// Returns a message sent to LSP for the note of the main diagnostic. -/// The message includes the main diagnostic to provide the necessary context -/// for the user to understand the note. -std::string noteMessage(const Diag &Main, const DiagBase &Note) { +std::string noteMessage(const Diag &Main, const DiagBase &Note, + const ClangdDiagnosticOptions &Opts) { std::string Result; llvm::raw_string_ostream OS(Result); OS << Note.Message; - OS << "\n\n"; - printDiag(OS, Main); + // If the client doesn't support structured links between the note and the + // original diagnostic, then emit the main diagnostic to give context. + if (!Opts.EmitRelatedLocations) { + OS << "\n\n"; + printDiag(OS, Main); + } OS.flush(); return capitalize(std::move(Result)); } @@ -275,39 +279,54 @@ void toLSPDiags( return Res; }; - { - clangd::Diagnostic Main = FillBasicFields(D); - Main.message = mainMessage(D, Opts.DisplayFixesCount); - if (!D.Name.empty()) - Main.code = D.Name; - switch (D.Source) { - case Diag::Clang: - Main.source = "clang"; - break; - case Diag::ClangTidy: - Main.source = "clang-tidy"; - break; - case Diag::Unknown: - break; - } - if (Opts.EmbedFixesInDiagnostics) { - Main.codeActions.emplace(); - for (const auto &Fix : D.Fixes) - Main.codeActions->push_back(toCodeAction(Fix, File)); - } - if (Opts.SendDiagnosticCategory && !D.Category.empty()) - Main.category = D.Category; - - OutFn(std::move(Main), D.Fixes); + clangd::Diagnostic Main = FillBasicFields(D); + Main.code = D.Name; + switch (D.Source) { + case Diag::Clang: + Main.source = "clang"; + break; + case Diag::ClangTidy: + Main.source = "clang-tidy"; + break; + case Diag::Unknown: + break; + } + if (Opts.EmbedFixesInDiagnostics) { + Main.codeActions.emplace(); + for (const auto &Fix : D.Fixes) + Main.codeActions->push_back(toCodeAction(Fix, File)); } + if (Opts.SendDiagnosticCategory && !D.Category.empty()) + Main.category = D.Category; - 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<Fix>()); + Main.message = mainMessage(D, Opts); + if (Opts.EmitRelatedLocations) { + Main.relatedInformation.emplace(); + for (auto &Note : D.Notes) { + if (!Note.AbsFile) { + vlog("Dropping note from unknown file: {0}", Note); + continue; + } + DiagnosticRelatedInformation RelInfo; + RelInfo.location.range = Note.Range; + RelInfo.location.uri = + URIForFile::canonicalize(*Note.AbsFile, File.file()); + RelInfo.message = noteMessage(D, Note, Opts); + Main.relatedInformation->push_back(std::move(RelInfo)); + } } + OutFn(std::move(Main), D.Fixes); + + // If we didn't emit the notes as relatedLocations, emit separate diagnostics + // so the user can find the locations easily. + if (!Opts.EmitRelatedLocations) + for (auto &Note : D.Notes) { + if (!Note.InsideMainFile) + continue; + clangd::Diagnostic Res = FillBasicFields(Note); + Res.message = noteMessage(D, Note, Opts); + OutFn(std::move(Res), llvm::ArrayRef<Fix>()); + } } int getSeverity(DiagnosticsEngine::Level L) { @@ -396,6 +415,9 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, D.Message = Message.str(); D.InsideMainFile = InsideMainFile; D.File = Info.getSourceManager().getFilename(Info.getLocation()); + auto &SM = Info.getSourceManager(); + D.AbsFile = getCanonicalPath( + SM.getFileEntryForID(SM.getFileID(Info.getLocation())), SM); D.Severity = DiagLevel; D.Category = DiagnosticIDs::getCategoryNameFromID( DiagnosticIDs::getCategoryNumberForDiag(Info.getID())) |

