clang-tools 10.0.0

Diagnostics.cpp
Go to the documentation of this file.
1 //===--- Diagnostics.cpp -----------------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "Diagnostics.h"
10 #include "../clang-tidy/ClangTidyDiagnosticConsumer.h"
11 #include "Compiler.h"
12 #include "Logger.h"
13 #include "Protocol.h"
14 #include "SourceCode.h"
15 #include "clang/Basic/AllDiagnostics.h"
16 #include "clang/Basic/Diagnostic.h"
17 #include "clang/Basic/DiagnosticIDs.h"
18 #include "clang/Basic/FileManager.h"
19 #include "clang/Basic/SourceLocation.h"
20 #include "clang/Basic/SourceManager.h"
21 #include "clang/Lex/Lexer.h"
22 #include "clang/Lex/Token.h"
23 #include "llvm/ADT/ArrayRef.h"
24 #include "llvm/ADT/DenseSet.h"
25 #include "llvm/ADT/Optional.h"
26 #include "llvm/ADT/STLExtras.h"
27 #include "llvm/ADT/SmallString.h"
28 #include "llvm/ADT/StringRef.h"
29 #include "llvm/ADT/Twine.h"
30 #include "llvm/Support/Capacity.h"
31 #include "llvm/Support/Path.h"
32 #include "llvm/Support/ScopedPrinter.h"
33 #include "llvm/Support/Signals.h"
34 #include "llvm/Support/raw_ostream.h"
35 #include <algorithm>
36 #include <cstddef>
37
38 namespace clang {
39 namespace clangd {
40 namespace {
41
42 const char *getDiagnosticCode(unsigned ID) {
43 switch (ID) {
44 #define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROPU, SFINAE, NOWERROR, \
45 SHOWINSYSHEADER, CATEGORY) \
46 case clang::diag::ENUM: \
47 return #ENUM;
48 #include "clang/Basic/DiagnosticASTKinds.inc"
49 #include "clang/Basic/DiagnosticAnalysisKinds.inc"
50 #include "clang/Basic/DiagnosticCommentKinds.inc"
51 #include "clang/Basic/DiagnosticCommonKinds.inc"
52 #include "clang/Basic/DiagnosticDriverKinds.inc"
53 #include "clang/Basic/DiagnosticFrontendKinds.inc"
54 #include "clang/Basic/DiagnosticLexKinds.inc"
55 #include "clang/Basic/DiagnosticParseKinds.inc"
56 #include "clang/Basic/DiagnosticRefactoringKinds.inc"
57 #include "clang/Basic/DiagnosticSemaKinds.inc"
58 #include "clang/Basic/DiagnosticSerializationKinds.inc"
59 #undef DIAG
60 default:
61 return nullptr;
62 }
63 }
64
65 bool mentionsMainFile(const Diag &D) {
66 if (D.InsideMainFile)
67 return true;
68 // Fixes are always in the main file.
69 if (!D.Fixes.empty())
70 return true;
71 for (auto &N : D.Notes) {
72 if (N.InsideMainFile)
73 return true;
74 }
75 return false;
76 }
77
78 bool isBlacklisted(const Diag &D) {
79 // clang will always fail parsing MS ASM, we don't link in desc + asm parser.
80 if (D.ID == clang::diag::err_msasm_unable_to_create_target ||
81 D.ID == clang::diag::err_msasm_unsupported_arch)
82 return true;
83 return false;
84 }
85
86 // Checks whether a location is within a half-open range.
87 // Note that clang also uses closed source ranges, which this can't handle!
88 bool locationInRange(SourceLocation L, CharSourceRange R,
89 const SourceManager &M) {
90 assert(R.isCharRange());
91 if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
92 M.getFileID(R.getBegin()) != M.getFileID(L))
93 return false;
94 return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
95 }
96
97 // Clang diags have a location (shown as ^) and 0 or more ranges (~~~~).
98 // LSP needs a single range.
99 Range diagnosticRange(const clang::Diagnostic &D, const LangOptions &L) {
100 auto &M = D.getSourceManager();
101 auto Loc = M.getFileLoc(D.getLocation());
102 for (const auto &CR : D.getRanges()) {
103 auto R = Lexer::makeFileCharRange(CR, M, L);
104 if (locationInRange(Loc, R, M))
105 return halfOpenToRange(M, R);
106 }
107 // The range may be given as a fixit hint instead.
108 for (const auto &F : D.getFixItHints()) {
109 auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
110 if (locationInRange(Loc, R, M))
111 return halfOpenToRange(M, R);
112 }
113 // If the token at the location is not a comment, we use the token.
114 // If we can't get the token at the location, fall back to using the location
115 auto R = CharSourceRange::getCharRange(Loc);
116 Token Tok;
117 if (!Lexer::getRawToken(Loc, Tok, M, L, true) && Tok.isNot(tok::comment)) {
118 R = CharSourceRange::getTokenRange(Tok.getLocation(), Tok.getEndLoc());
119 }
120 return halfOpenToRange(M, R);
121 }
122
123 // Returns whether the \p D is modified.
124 bool adjustDiagFromHeader(Diag &D, const clang::Diagnostic &Info,
125 const LangOptions &LangOpts) {
126 // We only report diagnostics with at least error severity from headers.
127 if (D.Severity < DiagnosticsEngine::Level::Error)
128 return false;
129
130 const SourceManager &SM = Info.getSourceManager();
131 const SourceLocation &DiagLoc = SM.getExpansionLoc(Info.getLocation());
132 SourceLocation IncludeInMainFile;
133 auto GetIncludeLoc = [&SM](SourceLocation SLoc) {
134 return SM.getIncludeLoc(SM.getFileID(SLoc));
135 };
136 for (auto IncludeLocation = GetIncludeLoc(DiagLoc); IncludeLocation.isValid();
137 IncludeLocation = GetIncludeLoc(IncludeLocation)) {
138 if (clangd::isInsideMainFile(IncludeLocation, SM)) {
139 IncludeInMainFile = IncludeLocation;
140 break;
141 }
142 }
143 if (IncludeInMainFile.isInvalid())
144 return false;
145
146 // Update diag to point at include inside main file.
147 D.File = SM.getFileEntryForID(SM.getMainFileID())->getName().str();
148 D.Range.start = sourceLocToPosition(SM, IncludeInMainFile);
149 D.Range.end = sourceLocToPosition(
150 SM, Lexer::getLocForEndOfToken(IncludeInMainFile, 0, SM, LangOpts));
151 D.InsideMainFile = true;
152
153 // Add a note that will point to real diagnostic.
154 const auto *FE = SM.getFileEntryForID(SM.getFileID(DiagLoc));
155 D.Notes.emplace_back();
156 Note &N = D.Notes.back();
157 N.AbsFile = FE->tryGetRealPathName();
158 N.File = FE->getName();
159 N.Message = "error occurred here";
160 N.Range = diagnosticRange(Info, LangOpts);
161
162 // Update message to mention original file.
163 D.Message = llvm::Twine("in included file: ", D.Message).str();
164 return true;
165 }
166
167 bool isInsideMainFile(const clang::Diagnostic &D) {
168 if (!D.hasSourceManager())
169 return false;
170
171 return clangd::isInsideMainFile(D.getLocation(), D.getSourceManager());
172 }
173
174 bool isNote(DiagnosticsEngine::Level L) {
175 return L == DiagnosticsEngine::Note || L == DiagnosticsEngine::Remark;
176 }
177
178 llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
179 switch (Lvl) {
180 case DiagnosticsEngine::Ignored:
181 return "ignored";
182 case DiagnosticsEngine::Note:
183 return "note";
184 case DiagnosticsEngine::Remark:
185 return "remark";
186 case DiagnosticsEngine::Warning:
187 return "warning";
188 case DiagnosticsEngine::Error:
189 return "error";
190 case DiagnosticsEngine::Fatal:
191 return "fatal error";
192 }
193 llvm_unreachable("unhandled DiagnosticsEngine::Level");
194 }
195
196 /// Prints a single diagnostic in a clang-like manner, the output includes
197 /// location, severity and error message. An example of the output message is:
198 ///
199 /// main.cpp:12:23: error: undeclared identifier
200 ///
201 /// For main file we only print the basename and for all other files we print
202 /// the filename on a separate line to provide a slightly more readable output
203 /// in the editors:
204 ///
205 /// dir1/dir2/dir3/../../dir4/header.h:12:23
206 /// error: undeclared identifier
207 void printDiag(llvm::raw_string_ostream &OS, const DiagBase &D) {
208 if (D.InsideMainFile) {
209 // Paths to main files are often taken from compile_command.json, where they
210 // are typically absolute. To reduce noise we print only basename for them,
211 // it should not be confusing and saves space.
212 OS << llvm::sys::path::filename(D.File) << ":";
213 } else {
214 OS << D.File << ":";
215 }
216 // Note +1 to line and character. clangd::Range is zero-based, but when
217 // printing for users we want one-based indexes.
218 auto Pos = D.Range.start;
219 OS << (Pos.line + 1) << ":" << (Pos.character + 1) << ":";
220 // The non-main-file paths are often too long, putting them on a separate
221 // line improves readability.
222 if (D.InsideMainFile)
223 OS << " ";
224 else
225 OS << "\n";
226 OS << diagLeveltoString(D.Severity) << ": " << D.Message;
227 }
228
229 /// Capitalizes the first word in the diagnostic's message.
230 std::string capitalize(std::string Message) {
231 if (!Message.empty())
232 Message[0] = llvm::toUpper(Message[0]);
233 return Message;
234 }
235
236 /// Returns a message sent to LSP for the main diagnostic in \p D.
237 /// This message may include notes, if they're not emitted in some other way.
238 /// Example output:
239 ///
240 /// no matching function for call to 'foo'
241 ///
242 /// main.cpp:3:5: note: candidate function not viable: requires 2 arguments
243 ///
244 /// dir1/dir2/dir3/../../dir4/header.h:12:23
245 /// note: candidate function not viable: requires 3 arguments
246 std::string mainMessage(const Diag &D, const ClangdDiagnosticOptions &Opts) {
247 std::string Result;
248 llvm::raw_string_ostream OS(Result);
249 OS << D.Message;
250 if (Opts.DisplayFixesCount && !D.Fixes.empty())
251 OS << " (" << (D.Fixes.size() > 1 ? "fixes" : "fix") << " available)";
252 // If notes aren't emitted as structured info, add them to the message.
253 if (!Opts.EmitRelatedLocations)
254 for (auto &Note : D.Notes) {
255 OS << "\n\n";
256 printDiag(OS, Note);
257 }
258 OS.flush();
259 return capitalize(std::move(Result));
260 }
261
262 /// Returns a message sent to LSP for the note of the main diagnostic.
263 std::string noteMessage(const Diag &Main, const DiagBase &Note,
264 const ClangdDiagnosticOptions &Opts) {
265 std::string Result;
266 llvm::raw_string_ostream OS(Result);
267 OS << Note.Message;
268 // If the client doesn't support structured links between the note and the
269 // original diagnostic, then emit the main diagnostic to give context.
270 if (!Opts.EmitRelatedLocations) {
271 OS << "\n\n";
272 printDiag(OS, Main);
273 }
274 OS.flush();
275 return capitalize(std::move(Result));
276 }
277 } // namespace
278
279 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const DiagBase &D) {
280 OS << "[";
281 if (!D.InsideMainFile)
282 OS << D.File << ":";
283 OS << D.Range.start << "-" << D.Range.end << "] ";
284
285 return OS << D.Message;
286 }
287
288 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Fix &F) {
289 OS << F.Message << " {";
290 const char *Sep = "";
291 for (const auto &Edit : F.Edits) {
292 OS << Sep << Edit;
293 Sep = ", ";
294 }
295 return OS << "}";
296 }
297
298 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Diag &D) {
299 OS << static_cast<const DiagBase &>(D);
300 if (!D.Notes.empty()) {
301 OS << ", notes: {";
302 const char *Sep = "";
303 for (auto &Note : D.Notes) {
304 OS << Sep << Note;
305 Sep = ", ";
306 }
307 OS << "}";
308 }
309 if (!D.Fixes.empty()) {
310 OS << ", fixes: {";
311 const char *Sep = "";
312 for (auto &Fix : D.Fixes) {
313 OS << Sep << Fix;
314 Sep = ", ";
315 }
316 }
317 return OS;
318 }
319
320 CodeAction toCodeAction(const Fix &F, const URIForFile &File) {
321 CodeAction Action;
322 Action.title = F.Message;
323 Action.kind = CodeAction::QUICKFIX_KIND;
324 Action.edit.emplace();
325 Action.edit->changes.emplace();
326 (*Action.edit->changes)[File.uri()] = {F.Edits.begin(), F.Edits.end()};
327 return Action;
328 }
329
330 void toLSPDiags(
331 const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts,
332 llvm::function_ref<void(clangd::Diagnostic, llvm::ArrayRef<Fix>)> OutFn) {
333 clangd::Diagnostic Main;
334 Main.severity = getSeverity(D.Severity);
335
336 // Main diagnostic should always refer to a range inside main file. If a
337 // diagnostic made it so for, it means either itself or one of its notes is
338 // inside main file.
339 if (D.InsideMainFile) {
340 Main.range = D.Range;
341 } else {
342 auto It =
343 llvm::find_if(D.Notes, [](const Note &N) { return N.InsideMainFile; });
344 assert(It != D.Notes.end() &&
345 "neither the main diagnostic nor notes are inside main file");
346 Main.range = It->Range;
347 }
348
349 Main.code = D.Name;
350 switch (D.Source) {
351 case Diag::Clang:
352 Main.source = "clang";
353 break;
354 case Diag::ClangTidy:
355 Main.source = "clang-tidy";
356 break;
357 case Diag::Unknown:
358 break;
359 }
360 if (Opts.EmbedFixesInDiagnostics) {
361 Main.codeActions.emplace();
362 for (const auto &Fix : D.Fixes)
363 Main.codeActions->push_back(toCodeAction(Fix, File));
364 }
365 if (Opts.SendDiagnosticCategory && !D.Category.empty())
366 Main.category = D.Category;
367
368 Main.message = mainMessage(D, Opts);
369 if (Opts.EmitRelatedLocations) {
370 Main.relatedInformation.emplace();
371 for (auto &Note : D.Notes) {
372 if (!Note.AbsFile) {
373 vlog("Dropping note from unknown file: {0}", Note);
374 continue;
375 }
376 DiagnosticRelatedInformation RelInfo;
377 RelInfo.location.range = Note.Range;
378 RelInfo.location.uri =
379 URIForFile::canonicalize(*Note.AbsFile, File.file());
380 RelInfo.message = noteMessage(D, Note, Opts);
381 Main.relatedInformation->push_back(std::move(RelInfo));
382 }
383 }
384 OutFn(std::move(Main), D.Fixes);
385
386 // If we didn't emit the notes as relatedLocations, emit separate diagnostics
387 // so the user can find the locations easily.
388 if (!Opts.EmitRelatedLocations)
389 for (auto &Note : D.Notes) {
390 if (!Note.InsideMainFile)
391 continue;
392 clangd::Diagnostic Res;
393 Res.severity = getSeverity(Note.Severity);
394 Res.range = Note.Range;
395 Res.message = noteMessage(D, Note, Opts);
396 OutFn(std::move(Res), llvm::ArrayRef<Fix>());
397 }
398 }
399
400 int getSeverity(DiagnosticsEngine::Level L) {
401 switch (L) {
402 case DiagnosticsEngine::Remark:
403 return 4;
404 case DiagnosticsEngine::Note:
405 return 3;
406 case DiagnosticsEngine::Warning:
407 return 2;
408 case DiagnosticsEngine::Fatal:
409 case DiagnosticsEngine::Error:
410 return 1;
411 case DiagnosticsEngine::Ignored:
412 return 0;
413 }
414 llvm_unreachable("Unknown diagnostic level!");
415 }
416
417 std::vector<Diag> StoreDiags::take(const clang::tidy::ClangTidyContext *Tidy) {
418 // Do not forget to emit a pending diagnostic if there is one.
419 flushLastDiag();
420
421 // Fill in name/source now that we have all the context needed to map them.
422 for (auto &Diag : Output) {
423 if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
424 // Warnings controlled by -Wfoo are better recognized by that name.
425 StringRef Warning = DiagnosticIDs::getWarningOptionForDiag(Diag.ID);
426 if (!Warning.empty()) {
427 Diag.Name = ("-W" + Warning).str();
428 } else {
429 StringRef Name(ClangDiag);
430 // Almost always an error, with a name like err_enum_class_reference.
431 // Drop the err_ prefix for brevity.
432 Name.consume_front("err_");
433 Diag.Name = Name;
434 }
435 Diag.Source = Diag::Clang;
436 continue;
437 }
438 if (Tidy != nullptr) {
439 std::string TidyDiag = Tidy->getCheckName(Diag.ID);
440 if (!TidyDiag.empty()) {
441 Diag.Name = std::move(TidyDiag);
442 Diag.Source = Diag::ClangTidy;
443 // clang-tidy bakes the name into diagnostic messages. Strip it out.
444 // It would be much nicer to make clang-tidy not do this.
445 auto CleanMessage = [&](std::string &Msg) {
446 StringRef Rest(Msg);
447 if (Rest.consume_back("]") && Rest.consume_back(Diag.Name) &&
448 Rest.consume_back(" ["))
449 Msg.resize(Rest.size());
450 };
451 CleanMessage(Diag.Message);
452 for (auto &Note : Diag.Notes)
453 CleanMessage(Note.Message);
454 for (auto &Fix : Diag.Fixes)
455 CleanMessage(Fix.Message);
456 continue;
457 }
458 }
459 }
460 // Deduplicate clang-tidy diagnostics -- some clang-tidy checks may emit
461 // duplicated messages due to various reasons (e.g. the check doesn't handle
462 // template instantiations well; clang-tidy alias checks).
463 std::set<std::pair<Range, std::string>> SeenDiags;
464 llvm::erase_if(Output, [&](const Diag& D) {
465 return !SeenDiags.emplace(D.Range, D.Message).second;
466 });
467 return std::move(Output);
468 }
469
470 void StoreDiags::BeginSourceFile(const LangOptions &Opts,
471 const Preprocessor *) {
472 LangOpts = Opts;
473 }
474
475 void StoreDiags::EndSourceFile() {
476 LangOpts = None;
477 }
478
479 /// Sanitizes a piece for presenting it in a synthesized fix message. Ensures
480 /// the result is not too large and does not contain newlines.
481 static void writeCodeToFixMessage(llvm::raw_ostream &OS, llvm::StringRef Code) {
482 constexpr unsigned MaxLen = 50;
483
484 // Only show the first line if there are many.
485 llvm::StringRef R = Code.split('\n').first;
486 // Shorten the message if it's too long.
487 R = R.take_front(MaxLen);
488
489 OS << R;
490 if (R.size() != Code.size())
491 OS << "…";
492 }
493
494 /// Fills \p D with all information, except the location-related bits.
495 /// Also note that ID and Name are not part of clangd::DiagBase and should be
496 /// set elsewhere.
497 static void fillNonLocationData(DiagnosticsEngine::Level DiagLevel,
498 const clang::Diagnostic &Info,
499 clangd::DiagBase &D) {
500 llvm::SmallString<64> Message;
501 Info.FormatDiagnostic(Message);
502
503 D.Message = Message.str();
504 D.Severity = DiagLevel;
505 D.Category = DiagnosticIDs::getCategoryNameFromID(
506 DiagnosticIDs::getCategoryNumberForDiag(Info.getID()))
507 .str();
508 }
509
510 void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
511 const clang::Diagnostic &Info) {
512 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
513
514 if (Info.getLocation().isInvalid()) {
515 // Handle diagnostics coming from command-line arguments. The source manager
516 // is *not* available at this point, so we cannot use it.
517 if (DiagLevel < DiagnosticsEngine::Level::Error) {
518 IgnoreDiagnostics::log(DiagLevel, Info);
519 return; // non-errors add too much noise, do not show them.
520 }
521
522 flushLastDiag();
523
524 LastDiag = Diag();
525 LastDiag->ID = Info.getID();
526 fillNonLocationData(DiagLevel, Info, *LastDiag);
527 LastDiag->InsideMainFile = true;
528 // Put it at the start of the main file, for a lack of a better place.
529 LastDiag->Range.start = Position{0, 0};
530 LastDiag->Range.end = Position{0, 0};
531 return;
532 }
533
534 if (!LangOpts || !Info.hasSourceManager()) {
535 IgnoreDiagnostics::log(DiagLevel, Info);
536 return;
537 }
538
539 bool InsideMainFile = isInsideMainFile(Info);
540 SourceManager &SM = Info.getSourceManager();
541
542 auto FillDiagBase = [&](DiagBase &D) {
543 fillNonLocationData(DiagLevel, Info, D);
544
545 D.InsideMainFile = InsideMainFile;
546 D.Range = diagnosticRange(Info, *LangOpts);
547 D.File = SM.getFilename(Info.getLocation());
548 D.AbsFile = getCanonicalPath(
549 SM.getFileEntryForID(SM.getFileID(Info.getLocation())), SM);
550 return D;
551 };
552
553 auto AddFix = [&](bool SyntheticMessage) -> bool {
554 assert(!Info.getFixItHints().empty() &&
555 "diagnostic does not have attached fix-its");
556 if (!InsideMainFile)
557 return false;
558
559 llvm::SmallVector<TextEdit, 1> Edits;
560 for (auto &FixIt : Info.getFixItHints()) {
561 // Follow clang's behavior, don't apply FixIt to the code in macros,
562 // we are less certain it is the right fix.
563 if (FixIt.RemoveRange.getBegin().isMacroID() ||
564 FixIt.RemoveRange.getEnd().isMacroID())
565 return false;
566 if (!isInsideMainFile(FixIt.RemoveRange.getBegin(), SM))
567 return false;
568 Edits.push_back(toTextEdit(FixIt, SM, *LangOpts));
569 }
570
571 llvm::SmallString<64> Message;
572 // If requested and possible, create a message like "change 'foo' to 'bar'".
573 if (SyntheticMessage && Info.getNumFixItHints() == 1) {
574 const auto &FixIt = Info.getFixItHint(0);
575 bool Invalid = false;
576 llvm::StringRef Remove =
577 Lexer::getSourceText(FixIt.RemoveRange, SM, *LangOpts, &Invalid);
578 llvm::StringRef Insert = FixIt.CodeToInsert;
579 if (!Invalid) {
580 llvm::raw_svector_ostream M(Message);
581 if (!Remove.empty() && !Insert.empty()) {
582 M << "change '";
583 writeCodeToFixMessage(M, Remove);
584 M << "' to '";
585 writeCodeToFixMessage(M, Insert);
586 M << "'";
587 } else if (!Remove.empty()) {
588 M << "remove '";
589 writeCodeToFixMessage(M, Remove);
590 M << "'";
591 } else if (!Insert.empty()) {
592 M << "insert '";
593 writeCodeToFixMessage(M, Insert);
594 M << "'";
595 }
596 // Don't allow source code to inject newlines into diagnostics.
597 std::replace(Message.begin(), Message.end(), '\n', ' ');
598 }
599 }
600 if (Message.empty()) // either !SytheticMessage, or we failed to make one.
601 Info.FormatDiagnostic(Message);
602 LastDiag->Fixes.push_back(Fix{Message.str(), std::move(Edits)});
603 return true;
604 };
605
606 if (!isNote(DiagLevel)) {
607 // Handle the new main diagnostic.
608 flushLastDiag();
609
610 if (Adjuster) {
611 DiagLevel = Adjuster(DiagLevel, Info);
612 if (DiagLevel == DiagnosticsEngine::Ignored) {
613 LastPrimaryDiagnosticWasSuppressed = true;
614 return;
615 }
616 }
617 LastPrimaryDiagnosticWasSuppressed = false;
618
619 LastDiag = Diag();
620 LastDiag->ID = Info.getID();
621 FillDiagBase(*LastDiag);
622 if (!InsideMainFile)
623 LastDiagWasAdjusted = adjustDiagFromHeader(*LastDiag, Info, *LangOpts);
624
625 if (!Info.getFixItHints().empty())
626 AddFix(true /* try to invent a message instead of repeating the diag */);
627 if (Fixer) {
628 auto ExtraFixes = Fixer(DiagLevel, Info);
629 LastDiag->Fixes.insert(LastDiag->Fixes.end(), ExtraFixes.begin(),
630 ExtraFixes.end());
631 }
632 } else {
633 // Handle a note to an existing diagnostic.
634
635 // If a diagnostic was suppressed due to the suppression filter,
636 // also suppress notes associated with it.
637 if (LastPrimaryDiagnosticWasSuppressed) {
638 return;
639 }
640
641 if (!LastDiag) {
642 assert(false && "Adding a note without main diagnostic");
643 IgnoreDiagnostics::log(DiagLevel, Info);
644 return;
645 }
646
647 if (!Info.getFixItHints().empty()) {
648 // A clang note with fix-it is not a separate diagnostic in clangd. We
649 // attach it as a Fix to the main diagnostic instead.
650 if (!AddFix(false /* use the note as the message */))
651 IgnoreDiagnostics::log(DiagLevel, Info);
652 } else {
653 // A clang note without fix-its corresponds to clangd::Note.
654 Note N;
655 FillDiagBase(N);
656
657 LastDiag->Notes.push_back(std::move(N));
658 }
659 }
660 }
661
662 void StoreDiags::flushLastDiag() {
663 if (!LastDiag)
664 return;
665 if (!isBlacklisted(*LastDiag) && mentionsMainFile(*LastDiag) &&
666 (!LastDiagWasAdjusted ||
667 // Only report the first diagnostic coming from each particular header.
668 IncludeLinesWithErrors.insert(LastDiag->Range.start.line).second)) {
669 Output.push_back(std::move(*LastDiag));
670 } else {
671 vlog("Dropped diagnostic: {0}: {1}", LastDiag->File, LastDiag->Message);
672 }
673 LastDiag.reset();
674 LastDiagWasAdjusted = false;
675 }
676
677 } // namespace clangd
678 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
clang::clangd::Diag::Clang
Definition: Diagnostics.h:90
clang::clangd::Diagnostic::code
std::string code
The diagnostic&#39;s code. Can be omitted.
Definition: Protocol.h:656
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::IgnoreDiagnostics::log
static void log(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info)
Definition: Compiler.cpp:20
clang::clangd::MessageType::Warning
A warning message.
clang::clangd::Range::start
Position start
The range&#39;s start position.
Definition: Protocol.h:158
clang::clangd::Location::range
Range range
Definition: Protocol.h:185
clang::clangd::Diag::ID
unsigned ID
Definition: Diagnostics.h:85
clang::clangd::MessageType::Error
An error message.
clang::clangd::ClangdDiagnosticOptions::EmbedFixesInDiagnostics
bool EmbedFixesInDiagnostics
If true, Clangd uses an LSP extension to embed the fixes with the diagnostics that are sent to the cl...
Definition: Diagnostics.h:34
clang::clangd::DiagBase
Contains basic information about a diagnostic.
Definition: Diagnostics.h:53
clang::clangd::toCodeAction
CodeAction toCodeAction(const Fix &F, const URIForFile &File)
Convert from Fix to LSP CodeAction.
Definition: Diagnostics.cpp:320
clang::clangd::Diagnostic::codeActions
llvm::Optional< std::vector< CodeAction > > codeActions
Clangd extension: code actions related to this diagnostic.
Definition: Protocol.h:678
clang::clangd::CodeAction::kind
llvm::Optional< std::string > kind
The kind of the code action.
Definition: Protocol.h:777
SourceCode.h
clang::clangd::CodeAction::title
std::string title
A short, human-readable, title for this code action.
Definition: Protocol.h:773
clang::clangd::DiagnosticRelatedInformation::location
Location location
The location of this related diagnostic information.
Definition: Protocol.h:640
clang::clangd::DiagnosticRelatedInformation
Represents a related message and source code location for a diagnostic.
Definition: Protocol.h:638
clang::clangd::isInsideMainFile
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
Definition: SourceCode.cpp:534
Protocol.h
clang::clangd::CodeAction
A code action represents a change that can be performed in code, e.g.
Definition: Protocol.h:771
clang::clangd::Position
Definition: Protocol.h:127
clang::tidy::modernize::Message
constexpr llvm::StringLiteral Message
Definition: UseTrailingReturnTypeCheck.cpp:101
clang::clangd::Location::uri
URIForFile uri
The text document&#39;s URI.
Definition: Protocol.h:184
clang::clangd::CodeAction::edit
llvm::Optional< WorkspaceEdit > edit
The workspace edit this code action performs.
Definition: Protocol.h:786
clang::clangd::TextDocumentSyncKind::None
Documents should not be synced at all.
clang::clangd::StoreDiags::BeginSourceFile
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override
Definition: Diagnostics.cpp:470
clang::clangd::vlog
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:67
clang::clangd::Diag::Name
std::string Name
Definition: Diagnostics.h:86
clang::clangd::replace
static std::string replace(llvm::StringRef Haystack, llvm::StringRef Needle, llvm::StringRef Repl)
Definition: TestIndex.cpp:30
clang::clangd::Diag
A top-level diagnostic that may have Notes and Fixes.
Definition: Diagnostics.h:84
clang::clangd::URIForFile::uri
std::string uri() const
Definition: Protocol.h:96
clang::clangd::Fix::Message
std::string Message
Message for the fix-it.
Definition: Diagnostics.h:73
clang::clangd::Diag::Fixes
std::vector< Fix > Fixes
Alternative fixes for this diagnostic, one should be chosen.
Definition: Diagnostics.h:96
clang::clangd::DiagBase::Category
std::string Category
Definition: Diagnostics.h:63
clang::clangd::Diagnostic::source
std::string source
A human-readable string describing the source of this diagnostic, e.g.
Definition: Protocol.h:660
clang::clangd::Fix::Edits
llvm::SmallVector< TextEdit, 1 > Edits
TextEdits from clang&#39;s fix-its. Must be non-empty.
Definition: Diagnostics.h:75
clang::clangd::toTextEdit
TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, const LangOptions &L)
Definition: SourceCode.cpp:661
clang::clangd::DiagBase::File
std::string File
Definition: Diagnostics.h:57
clang::clangd::toLSPDiags
void toLSPDiags(const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts, llvm::function_ref< void(clangd::Diagnostic, llvm::ArrayRef< Fix >)> OutFn)
Conversion to LSP diagnostics.
Definition: Diagnostics.cpp:330
clang::clangd::getSeverity
int getSeverity(DiagnosticsEngine::Level L)
Convert from clang diagnostic level to LSP severity.
Definition: Diagnostics.cpp:400
clang::clangd::StoreDiags::HandleDiagnostic
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override
Definition: Diagnostics.cpp:510
Compiler.h
Action
llvm::unique_function< void()> Action
Definition: TUScheduler.cpp:234
Diagnostics.h
clang::clangd::Diagnostic::category
llvm::Optional< std::string > category
The diagnostic&#39;s category.
Definition: Protocol.h:673
clang::clangd::DiagBase::Range
clangd::Range Range
Definition: Diagnostics.h:61
clang::clangd::DiagBase::Severity
DiagnosticsEngine::Level Severity
Definition: Diagnostics.h:62
clang::clangd::ClangdDiagnosticOptions
Definition: Diagnostics.h:31
clang::clangd::URIForFile::canonicalize
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
Definition: Protocol.cpp:32
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::clangd::Diag::ClangTidy
Definition: Diagnostics.h:91
clang::clangd::DiagnosticRelatedInformation::message
std::string message
The message of this related diagnostic information.
Definition: Protocol.h:642
clang::clangd::Fix
Represents a single fix-it that editor can apply to fix the error.
Definition: Diagnostics.h:71
clang::clangd::URIForFile
Definition: Protocol.h:73
clang::clangd::getTokenRange
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
Pos
Position Pos
Definition: SourceCode.cpp:772
clang::clangd::sourceLocToPosition
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
Definition: SourceCode.cpp:200
clang::clangd::fillNonLocationData
static void fillNonLocationData(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info, clangd::DiagBase &D)
Fills D with all information, except the location-related bits.
Definition: Diagnostics.cpp:497
clang::clangd::Position::line
int line
Line position in a document (zero-based).
Definition: Protocol.h:129
Logger.h
clang::clangd::Position::character
int character
Character offset on a line in a document (zero-based).
Definition: Protocol.h:134
clang::clangd::Diag::Notes
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
Definition: Diagnostics.h:94
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::DiagBase::InsideMainFile
bool InsideMainFile
Definition: Diagnostics.h:66
clang::clangd::Diagnostic::message
std::string message
The diagnostic&#39;s message.
Definition: Protocol.h:663
clang::clangd::ClangdDiagnosticOptions::EmitRelatedLocations
bool EmitRelatedLocations
If true, Clangd uses the relatedInformation field to include other locations (in particular attached ...
Definition: Diagnostics.h:39
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::clangd::DiagBase::Message
std::string Message
Definition: Diagnostics.h:54
clang::clangd::CodeAction::QUICKFIX_KIND
static const llvm::StringLiteral QUICKFIX_KIND
Definition: Protocol.h:778
clang::clangd::getCanonicalPath
llvm::Optional< std::string > getCanonicalPath(const FileEntry *F, const SourceManager &SourceMgr)
Get the canonical path of F.
Definition: SourceCode.cpp:622
clang::tidy::ClangTidyContext::getCheckName
std::string getCheckName(unsigned DiagnosticID) const
Returns the name of the clang-tidy check which produced this diagnostic ID.
Definition: ClangTidyDiagnosticConsumer.cpp:226
clang::clangd::Diagnostic::severity
int severity
The diagnostic&#39;s severity.
Definition: Protocol.h:653
clang::clangd::ClangdDiagnosticOptions::SendDiagnosticCategory
bool SendDiagnosticCategory
If true, Clangd uses an LSP extension to send the diagnostic&#39;s category to the client.
Definition: Diagnostics.h:45
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
clang::clangd::StoreDiags::EndSourceFile
void EndSourceFile() override
Definition: Diagnostics.cpp:475
FixIt
llvm::Optional< FixItHint > FixIt
Definition: UppercaseLiteralSuffixCheck.cpp:62
clang::clangd::Diagnostic
Definition: Protocol.h:647
clang::clangd::Diagnostic::range
Range range
The range at which the message applies.
Definition: Protocol.h:649
clang::clangd::Diag::Source
enum clang::clangd::Diag::@0 Source
Fix
static cl::opt< bool > Fix("fix", cl::desc(R"( Apply suggested fixes. Without -fix-errors clang-tidy will bail out if any compilation errors were found. )"), cl::init(false), cl::cat(ClangTidyCategory))
clang::clangd::Diag::Unknown
Definition: Diagnostics.h:89
clang::clangd::Range::end
Position end
The range&#39;s end position.
Definition: Protocol.h:161
clang::clangd::operator<<
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
Definition: CodeComplete.cpp:1850
clang::clangd::Edit
A set of edits generated for a single file.
Definition: SourceCode.h:204
clang::clangd::StoreDiags::take
std::vector< Diag > take(const clang::tidy::ClangTidyContext *Tidy=nullptr)
Definition: Diagnostics.cpp:417
clang::clangd::Diagnostic::relatedInformation
llvm::Optional< std::vector< DiagnosticRelatedInformation > > relatedInformation
An array of related diagnostic information, e.g.
Definition: Protocol.h:667
clang::clangd::writeCodeToFixMessage
static void writeCodeToFixMessage(llvm::raw_ostream &OS, llvm::StringRef Code)
Sanitizes a piece for presenting it in a synthesized fix message.
Definition: Diagnostics.cpp:481
clang::clangd::DiagBase::AbsFile
llvm::Optional< std::string > AbsFile
Definition: Diagnostics.h:59
clang::clangd::halfOpenToRange
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R)
Definition: SourceCode.cpp:581
clang::clangd::URIForFile::file
llvm::StringRef file() const
Retrieves absolute path to the file.
Definition: Protocol.h:93