Skip to content

Commit 641caa5

Browse files
committed
[clangd] Include textual diagnostic ID as Diagnostic.code.
Reviewers: kadircet Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, jdoerfert, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D58291 llvm-svn: 358575
1 parent 9daacec commit 641caa5

14 files changed

+137
-33
lines changed

clang-tools-extra/clangd/ClangdUnit.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,7 @@ ParsedAST::build(std::unique_ptr<CompilerInvocation> CI,
371371
// So just inform the preprocessor of EOF, while keeping everything alive.
372372
Clang->getPreprocessor().EndSourceFile();
373373

374-
std::vector<Diag> Diags = ASTDiags.take();
375-
// Populate diagnostic source.
376-
for (auto &D : Diags)
377-
D.S =
378-
!CTContext->getCheckName(D.ID).empty() ? Diag::ClangTidy : Diag::Clang;
374+
std::vector<Diag> Diags = ASTDiags.take(CTContext.getPointer());
379375
// Add diagnostics from the preamble, if any.
380376
if (Preamble)
381377
Diags.insert(Diags.begin(), Preamble->Diags.begin(), Preamble->Diags.end());
@@ -544,8 +540,6 @@ buildPreamble(PathRef FileName, CompilerInvocation &CI,
544540
vlog("Built preamble of size {0} for file {1}", BuiltPreamble->getSize(),
545541
FileName);
546542
std::vector<Diag> Diags = PreambleDiagnostics.take();
547-
for (auto &Diag : Diags)
548-
Diag.S = Diag::Clang;
549543
return std::make_shared<PreambleData>(
550544
std::move(*BuiltPreamble), std::move(Diags),
551545
SerializedDeclsCollector.takeIncludes(), std::move(StatCache),

clang-tools-extra/clangd/Diagnostics.cpp

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "Diagnostics.h"
10+
#include "../clang-tidy/ClangTidyDiagnosticConsumer.h"
1011
#include "Compiler.h"
1112
#include "Logger.h"
1213
#include "SourceCode.h"
14+
#include "clang/Basic/AllDiagnostics.h"
15+
#include "clang/Basic/DiagnosticIDs.h"
1316
#include "clang/Basic/SourceManager.h"
1417
#include "clang/Lex/Lexer.h"
1518
#include "llvm/Support/Capacity.h"
@@ -18,9 +21,31 @@
1821

1922
namespace clang {
2023
namespace clangd {
21-
2224
namespace {
2325

26+
const char *getDiagnosticCode(unsigned ID) {
27+
switch (ID) {
28+
#define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROPU, SFINAE, NOWERROR, \
29+
SHOWINSYSHEADER, CATEGORY) \
30+
case clang::diag::ENUM: \
31+
return #ENUM;
32+
#include "clang/Basic/DiagnosticASTKinds.inc"
33+
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
34+
#include "clang/Basic/DiagnosticCommentKinds.inc"
35+
#include "clang/Basic/DiagnosticCommonKinds.inc"
36+
#include "clang/Basic/DiagnosticDriverKinds.inc"
37+
#include "clang/Basic/DiagnosticFrontendKinds.inc"
38+
#include "clang/Basic/DiagnosticLexKinds.inc"
39+
#include "clang/Basic/DiagnosticParseKinds.inc"
40+
#include "clang/Basic/DiagnosticRefactoringKinds.inc"
41+
#include "clang/Basic/DiagnosticSemaKinds.inc"
42+
#include "clang/Basic/DiagnosticSerializationKinds.inc"
43+
#undef DIAG
44+
default:
45+
return nullptr;
46+
}
47+
}
48+
2449
bool mentionsMainFile(const Diag &D) {
2550
if (D.InsideMainFile)
2651
return true;
@@ -253,6 +278,18 @@ void toLSPDiags(
253278
{
254279
clangd::Diagnostic Main = FillBasicFields(D);
255280
Main.message = mainMessage(D, Opts.DisplayFixesCount);
281+
if (!D.Name.empty())
282+
Main.code = D.Name;
283+
switch (D.Source) {
284+
case Diag::Clang:
285+
Main.source = "clang";
286+
break;
287+
case Diag::ClangTidy:
288+
Main.source = "clang-tidy";
289+
break;
290+
case Diag::Unknown:
291+
break;
292+
}
256293
if (Opts.EmbedFixesInDiagnostics) {
257294
Main.codeActions.emplace();
258295
for (const auto &Fix : D.Fixes)
@@ -290,7 +327,25 @@ int getSeverity(DiagnosticsEngine::Level L) {
290327
llvm_unreachable("Unknown diagnostic level!");
291328
}
292329

293-
std::vector<Diag> StoreDiags::take() { return std::move(Output); }
330+
std::vector<Diag> StoreDiags::take(const clang::tidy::ClangTidyContext *Tidy) {
331+
// Fill in name/source now that we have all the context needed to map them.
332+
for (auto &Diag : Output) {
333+
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
334+
Diag.Name = ClangDiag;
335+
Diag.Source = Diag::Clang;
336+
continue;
337+
}
338+
if (Tidy != nullptr) {
339+
std::string TidyDiag = Tidy->getCheckName(Diag.ID);
340+
if (!TidyDiag.empty()) {
341+
Diag.Name = std::move(TidyDiag);
342+
Diag.Source = Diag::ClangTidy;
343+
continue;
344+
}
345+
}
346+
}
347+
return std::move(Output);
348+
}
294349

295350
void StoreDiags::BeginSourceFile(const LangOptions &Opts,
296351
const Preprocessor *) {

clang-tools-extra/clangd/Diagnostics.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
#include <string>
2121

2222
namespace clang {
23+
namespace tidy {
24+
class ClangTidyContext;
25+
} // namespace tidy
2326
namespace clangd {
2427

2528
struct ClangdDiagnosticOptions {
@@ -68,14 +71,14 @@ struct Note : DiagBase {};
6871

6972
/// A top-level diagnostic that may have Notes and Fixes.
7073
struct Diag : DiagBase {
71-
// Diagnostic enum ID.
72-
unsigned ID;
74+
unsigned ID; // e.g. member of clang::diag, or clang-tidy assigned ID.
75+
std::string Name; // if ID was recognized.
7376
// The source of this diagnostic.
74-
enum Source {
77+
enum {
78+
Unknown,
7579
Clang,
7680
ClangTidy,
77-
};
78-
Source S = Clang;
81+
} Source = Unknown;
7982
/// Elaborate on the problem, usually pointing to a related piece of code.
8083
std::vector<Note> Notes;
8184
/// *Alternative* fixes for this diagnostic, one should be chosen.
@@ -104,7 +107,8 @@ int getSeverity(DiagnosticsEngine::Level L);
104107
/// the diag itself nor its notes are in the main file).
105108
class StoreDiags : public DiagnosticConsumer {
106109
public:
107-
std::vector<Diag> take();
110+
// The ClangTidyContext populates Source and Name for clang-tidy diagnostics.
111+
std::vector<Diag> take(const clang::tidy::ClangTidyContext *Tidy = nullptr);
108112

109113
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override;
110114
void EndSourceFile() override;

clang-tools-extra/clangd/Protocol.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ llvm::json::Value toJSON(const Diagnostic &D) {
429429
Diag["category"] = *D.category;
430430
if (D.codeActions)
431431
Diag["codeActions"] = D.codeActions;
432+
if (!D.code.empty())
433+
Diag["code"] = D.code;
434+
if (!D.source.empty())
435+
Diag["source"] = D.source;
432436
return std::move(Diag);
433437
}
434438

@@ -438,6 +442,8 @@ bool fromJSON(const llvm::json::Value &Params, Diagnostic &R) {
438442
return false;
439443
O.map("severity", R.severity);
440444
O.map("category", R.category);
445+
O.map("code", R.code);
446+
O.map("source", R.source);
441447
return true;
442448
}
443449

clang-tools-extra/clangd/Protocol.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -592,13 +592,11 @@ struct Diagnostic {
592592
int severity = 0;
593593

594594
/// The diagnostic's code. Can be omitted.
595-
/// Note: Not currently used by clangd
596-
// std::string code;
595+
std::string code;
597596

598597
/// A human-readable string describing the source of this
599598
/// diagnostic, e.g. 'typescript' or 'super lint'.
600-
/// Note: Not currently used by clangd
601-
// std::string source;
599+
std::string source;
602600

603601
/// The diagnostic's message.
604602
std::string message;

clang-tools-extra/test/clangd/compile-commands-path-in-initialize.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
# CHECK-NEXT: "params": {
2222
# CHECK-NEXT: "diagnostics": [
2323
# CHECK-NEXT: {
24+
# CHECK-NEXT: "code": "warn_pragma_message",
2425
# CHECK-NEXT: "message": "MACRO is one",
2526
---
2627
{"jsonrpc":"2.0","id":10000,"method":"shutdown"}

clang-tools-extra/test/clangd/diagnostic-category.test

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# CHECK-NEXT: "diagnostics": [
88
# CHECK-NEXT: {
99
# CHECK-NEXT: "category": "Semantic Issue",
10+
# CHECK-NEXT: "code": "err_use_with_wrong_tag",
1011
# CHECK-NEXT: "message": "Use of 'Point' with tag type that does not match previous declaration (fix available)\n\nfoo.c:1:8: note: previous use is here",
1112
# CHECK-NEXT: "range": {
1213
# CHECK-NEXT: "end": {
@@ -18,7 +19,8 @@
1819
# CHECK-NEXT: "line": 0
1920
# CHECK-NEXT: }
2021
# CHECK-NEXT: },
21-
# CHECK-NEXT: "severity": 1
22+
# CHECK-NEXT: "severity": 1,
23+
# CHECK-NEXT: "source": "clang"
2224
# CHECK-NEXT: },
2325
# CHECK-NEXT: {
2426
# CHECK-NEXT: "message": "Previous use is here\n\nfoo.c:1:18: error: use of 'Point' with tag type that does not match previous declaration",

clang-tools-extra/test/clangd/diagnostics.test

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
1+
# RUN: clangd -lit-test -clang-tidy-checks=bugprone-sizeof-expression < %s | FileCheck -strict-whitespace %s
22
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
33
---
4-
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"void main() {}"}}}
4+
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"void main() {\n(void)sizeof(42);\n}"}}}
55
# CHECK: "method": "textDocument/publishDiagnostics",
66
# CHECK-NEXT: "params": {
77
# CHECK-NEXT: "diagnostics": [
88
# CHECK-NEXT: {
9+
# CHECK-NEXT: "code": "ext_main_returns_nonint",
910
# CHECK-NEXT: "message": "Return type of 'main' is not 'int' (fix available)",
1011
# CHECK-NEXT: "range": {
1112
# CHECK-NEXT: "end": {
@@ -17,7 +18,24 @@
1718
# CHECK-NEXT: "line": 0
1819
# CHECK-NEXT: }
1920
# CHECK-NEXT: },
20-
# CHECK-NEXT: "severity": 2
21+
# CHECK-NEXT: "severity": 2,
22+
# CHECK-NEXT: "source": "clang"
23+
# CHECK-NEXT: },
24+
# CHECK-NEXT: {
25+
# CHECK-NEXT: "code": "bugprone-sizeof-expression",
26+
# CHECK-NEXT: "message": "Suspicious usage of 'sizeof(K)'; did you mean 'K'? [bugprone-sizeof-expression]",
27+
# CHECK-NEXT: "range": {
28+
# CHECK-NEXT: "end": {
29+
# CHECK-NEXT: "character": 12,
30+
# CHECK-NEXT: "line": 1
31+
# CHECK-NEXT: },
32+
# CHECK-NEXT: "start": {
33+
# CHECK-NEXT: "character": 6,
34+
# CHECK-NEXT: "line": 1
35+
# CHECK-NEXT: }
36+
# CHECK-NEXT: },
37+
# CHECK-NEXT: "severity": 2,
38+
# CHECK-NEXT: "source": "clang-tidy"
2139
# CHECK-NEXT: }
2240
# CHECK-NEXT: ],
2341
# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"

clang-tools-extra/test/clangd/did-change-configuration-params.test

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
# CHECK-NEXT: "params": {
2525
# CHECK-NEXT: "diagnostics": [
2626
# CHECK-NEXT: {
27+
# CHECK-NEXT: "code": "warn_uninit_var",
2728
# CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here (fix available)",
2829
# CHECK-NEXT: "range": {
2930
# CHECK-NEXT: "end": {
@@ -35,7 +36,8 @@
3536
# CHECK-NEXT: "line": 0
3637
# CHECK-NEXT: }
3738
# CHECK-NEXT: },
38-
# CHECK-NEXT: "severity": 1
39+
# CHECK-NEXT: "severity": 1,
40+
# CHECK-NEXT: "source": "clang"
3941
# CHECK-NEXT: }
4042
# CHECK-NEXT: ],
4143
# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"

clang-tools-extra/test/clangd/execute-command.test

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# CHECK-NEXT: "params": {
77
# CHECK-NEXT: "diagnostics": [
88
# CHECK-NEXT: {
9+
# CHECK-NEXT: "code": "warn_condition_is_assignment",
910
# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses (fixes available)",
1011
# CHECK-NEXT: "range": {
1112
# CHECK-NEXT: "end": {
@@ -17,7 +18,8 @@
1718
# CHECK-NEXT: "line": 0
1819
# CHECK-NEXT: }
1920
# CHECK-NEXT: },
20-
# CHECK-NEXT: "severity": 2
21+
# CHECK-NEXT: "severity": 2,
22+
# CHECK-NEXT: "source": "clang"
2123
# CHECK-NEXT: }
2224
# CHECK-NEXT: ],
2325
# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"

0 commit comments

Comments
 (0)