sammccall created this revision.
sammccall added a reviewer: adamcz.
Herald added subscribers: usaxena95, kadircet, arphaman.
Herald added a project: All.
sammccall requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.
LSP supports Diagnostic.codeInformation since 3.16.
In VSCode, this turns the code (e.g. "unused-includes" or "bugprone-foo") into
a clickable link that opens the docs in a web browser.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D126065
Files:
clang-tools-extra/clangd/Diagnostics.cpp
clang-tools-extra/clangd/Diagnostics.h
clang-tools-extra/clangd/Protocol.cpp
clang-tools-extra/clangd/Protocol.h
clang-tools-extra/clangd/test/diagnostics-tidy.test
clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
Index: clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -1828,13 +1828,17 @@
Cfg.Diagnostics.Includes.IgnoreHeader.emplace_back(
[](llvm::StringRef Header) { return Header.endswith("ignore.h"); });
WithContextValue WithCfg(Config::Key, std::move(Cfg));
+ auto AST = TU.build();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ *AST.getDiagnostics(),
UnorderedElementsAre(AllOf(
Diag(Test.range("diag"),
"included header unused.h is not used directly"),
withTag(DiagnosticTag::Unnecessary), diagSource(Diag::Clangd),
withFix(Fix(Test.range("fix"), "", "remove #include directive")))));
+ auto &Diag = AST.getDiagnostics()->front();
+ EXPECT_EQ(getDiagnosticDocURI(Diag.Source, Diag.ID, Diag.Name),
+ std::string("https://clangd.llvm.org/guides/include-cleaner"));
Cfg.Diagnostics.SuppressAll = true;
WithContextValue SuppressAllWithCfg(Config::Key, std::move(Cfg));
EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
Index: clang-tools-extra/clangd/test/diagnostics-tidy.test
===================================================================
--- clang-tools-extra/clangd/test/diagnostics-tidy.test
+++ clang-tools-extra/clangd/test/diagnostics-tidy.test
@@ -8,6 +8,9 @@
# CHECK-NEXT: "diagnostics": [
# CHECK-NEXT: {
# CHECK-NEXT: "code": "bugprone-sizeof-expression",
+# CHECK-NEXT: "codeDescription": {
+# CHECK-NEXT: "href": "https://clang.llvm.org/extra/clang-tidy/checks/bugprone-sizeof-expression.html"
+# CHECK-NEXT: },
# CHECK-NEXT: "message": "Suspicious usage of 'sizeof(K)'; did you mean 'K'?",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -835,6 +835,13 @@
};
llvm::json::Value toJSON(DiagnosticTag Tag);
+/// Structure to capture a description for an error code.
+struct CodeDescription {
+ /// An URI to open with more information about the diagnostic error.
+ std::string href;
+};
+llvm::json::Value toJSON(const CodeDescription &);
+
struct CodeAction;
struct Diagnostic {
/// The range at which the message applies.
@@ -847,6 +854,9 @@
/// The diagnostic's code. Can be omitted.
std::string code;
+ /// An optional property to describe the error code.
+ llvm::Optional<CodeDescription> codeDescription;
+
/// A human-readable string describing the source of this
/// diagnostic, e.g. 'typescript' or 'super lint'.
std::string source;
@@ -874,7 +884,7 @@
/// A data entry field that is preserved between a
/// `textDocument/publishDiagnostics` notification
- /// and`textDocument/codeAction` request.
+ /// and `textDocument/codeAction` request.
/// Mutating users should associate their data with a unique key they can use
/// to retrieve later on.
llvm::json::Object data;
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -592,6 +592,10 @@
llvm::json::Value toJSON(DiagnosticTag Tag) { return static_cast<int>(Tag); }
+llvm::json::Value toJSON(const CodeDescription &D) {
+ return llvm::json::Object{{"href", D.href}};
+}
+
llvm::json::Value toJSON(const Diagnostic &D) {
llvm::json::Object Diag{
{"range", D.range},
@@ -604,6 +608,8 @@
Diag["codeActions"] = D.codeActions;
if (!D.code.empty())
Diag["code"] = D.code;
+ if (D.codeDescription.hasValue())
+ Diag["codeDescription"] = *D.codeDescription;
if (!D.source.empty())
Diag["source"] = D.source;
if (D.relatedInformation)
Index: clang-tools-extra/clangd/Diagnostics.h
===================================================================
--- clang-tools-extra/clangd/Diagnostics.h
+++ clang-tools-extra/clangd/Diagnostics.h
@@ -126,6 +126,10 @@
/// Convert from clang diagnostic level to LSP severity.
int getSeverity(DiagnosticsEngine::Level L);
+/// Returns a URI providing more information about a particular diagnostic.
+llvm::Optional<std::string> getDiagnosticDocURI(Diag::DiagSource, unsigned ID,
+ llvm::StringRef Name);
+
/// StoreDiags collects the diagnostics that can later be reported by
/// clangd. It groups all notes for a diagnostic into a single Diag
/// and filters out diagnostics that don't mention the main file (i.e. neither
Index: clang-tools-extra/clangd/Diagnostics.cpp
===================================================================
--- clang-tools-extra/clangd/Diagnostics.cpp
+++ clang-tools-extra/clangd/Diagnostics.cpp
@@ -477,6 +477,10 @@
}
Main.code = D.Name;
+ if (auto URI = getDiagnosticDocURI(D.Source, D.ID, D.Name)) {
+ Main.codeDescription.emplace();
+ Main.codeDescription->href = std::move(*URI);
+ }
switch (D.Source) {
case Diag::Clang:
Main.source = "clang";
@@ -903,5 +907,31 @@
return Code;
}
+llvm::Optional<std::string> getDiagnosticDocURI(Diag::DiagSource Source,
+ unsigned ID,
+ llvm::StringRef Name) {
+ switch (Source) {
+ case Diag::Unknown:
+ break;
+ case Diag::Clang:
+ // There is a page listing many warning flags, but it provides too little
+ // information to be worth linking.
+ // https://clang.llvm.org/docs/DiagnosticsReference.html
+ break;
+ case Diag::ClangTidy:
+ return {("https://clang.llvm.org/extra/clang-tidy-checks/" + Name + ".html")
+ .str()};
+ case Diag::Clangd:
+ if (Name == "unused-includes")
+ return {"https://clangd.llvm.org/guides/include-cleaner"};
+ break;
+ case Diag::ClangdConfig:
+ // FIXME: we should link to https://clangd.llvm.org/config
+ // However we have no diagnostic codes, which the link should describe!
+ break;
+ }
+ return llvm::None;
+}
+
} // namespace clangd
} // namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits