clang-tools  16.0.0git
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 "Protocol.h"
13 #include "SourceCode.h"
14 #include "support/Logger.h"
15 #include "clang/Basic/AllDiagnostics.h" // IWYU pragma: keep
16 #include "clang/Basic/Diagnostic.h"
17 #include "clang/Basic/DiagnosticIDs.h"
18 #include "clang/Basic/SourceLocation.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "clang/Lex/Lexer.h"
21 #include "clang/Lex/Token.h"
22 #include "llvm/ADT/ArrayRef.h"
23 #include "llvm/ADT/DenseSet.h"
24 #include "llvm/ADT/Optional.h"
25 #include "llvm/ADT/STLExtras.h"
26 #include "llvm/ADT/ScopeExit.h"
27 #include "llvm/ADT/SmallString.h"
28 #include "llvm/ADT/SmallVector.h"
29 #include "llvm/ADT/StringRef.h"
30 #include "llvm/ADT/Twine.h"
31 #include "llvm/Support/Path.h"
32 #include "llvm/Support/raw_ostream.h"
33 #include <algorithm>
34 #include <cassert>
35 #include <cstddef>
36 #include <vector>
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, SHOWINSYSMACRO, DEFERRABLE, 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 isExcluded(unsigned DiagID) {
79  // clang will always fail parsing MS ASM, we don't link in desc + asm parser.
80  if (DiagID == clang::diag::err_msasm_unable_to_create_target ||
81  DiagID == 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 // Try to find a location in the main-file to report the diagnostic D.
124 // Returns a description like "in included file", or nullptr on failure.
125 const char *getMainFileRange(const Diag &D, const SourceManager &SM,
126  SourceLocation DiagLoc, Range &R) {
127  // Look for a note in the main file indicating template instantiation.
128  for (const auto &N : D.Notes) {
129  if (N.InsideMainFile) {
130  switch (N.ID) {
131  case diag::note_template_class_instantiation_was_here:
132  case diag::note_template_class_explicit_specialization_was_here:
133  case diag::note_template_class_instantiation_here:
134  case diag::note_template_member_class_here:
135  case diag::note_template_member_function_here:
136  case diag::note_function_template_spec_here:
137  case diag::note_template_static_data_member_def_here:
138  case diag::note_template_variable_def_here:
139  case diag::note_template_enum_def_here:
140  case diag::note_template_nsdmi_here:
141  case diag::note_template_type_alias_instantiation_here:
142  case diag::note_template_exception_spec_instantiation_here:
143  case diag::note_template_requirement_instantiation_here:
144  case diag::note_evaluating_exception_spec_here:
145  case diag::note_default_arg_instantiation_here:
146  case diag::note_default_function_arg_instantiation_here:
147  case diag::note_explicit_template_arg_substitution_here:
148  case diag::note_function_template_deduction_instantiation_here:
149  case diag::note_deduced_template_arg_substitution_here:
150  case diag::note_prior_template_arg_substitution:
151  case diag::note_template_default_arg_checking:
152  case diag::note_concept_specialization_here:
153  case diag::note_nested_requirement_here:
154  case diag::note_checking_constraints_for_template_id_here:
155  case diag::note_checking_constraints_for_var_spec_id_here:
156  case diag::note_checking_constraints_for_class_spec_id_here:
157  case diag::note_checking_constraints_for_function_here:
158  case diag::note_constraint_substitution_here:
159  case diag::note_constraint_normalization_here:
160  case diag::note_parameter_mapping_substitution_here:
161  R = N.Range;
162  return "in template";
163  default:
164  break;
165  }
166  }
167  }
168  // Look for where the file with the error was #included.
169  auto GetIncludeLoc = [&SM](SourceLocation SLoc) {
170  return SM.getIncludeLoc(SM.getFileID(SLoc));
171  };
172  for (auto IncludeLocation = GetIncludeLoc(SM.getExpansionLoc(DiagLoc));
173  IncludeLocation.isValid();
174  IncludeLocation = GetIncludeLoc(IncludeLocation)) {
175  if (clangd::isInsideMainFile(IncludeLocation, SM)) {
176  R.start = sourceLocToPosition(SM, IncludeLocation);
177  R.end = sourceLocToPosition(
178  SM,
179  Lexer::getLocForEndOfToken(IncludeLocation, 0, SM, LangOptions()));
180  return "in included file";
181  }
182  }
183  return nullptr;
184 }
185 
186 // Place the diagnostic the main file, rather than the header, if possible:
187 // - for errors in included files, use the #include location
188 // - for errors in template instantiation, use the instantiation location
189 // In both cases, add the original header location as a note.
190 bool tryMoveToMainFile(Diag &D, FullSourceLoc DiagLoc) {
191  const SourceManager &SM = DiagLoc.getManager();
192  DiagLoc = DiagLoc.getExpansionLoc();
193  Range R;
194  const char *Prefix = getMainFileRange(D, SM, DiagLoc, R);
195  if (!Prefix)
196  return false;
197 
198  // Add a note that will point to real diagnostic.
199  auto FE = *SM.getFileEntryRefForID(SM.getFileID(DiagLoc));
200  D.Notes.emplace(D.Notes.begin());
201  Note &N = D.Notes.front();
202  N.AbsFile = std::string(FE.getFileEntry().tryGetRealPathName());
203  N.File = std::string(FE.getName());
204  N.Message = "error occurred here";
205  N.Range = D.Range;
206 
207  // Update diag to point at include inside main file.
208  D.File = SM.getFileEntryRefForID(SM.getMainFileID())->getName().str();
209  D.Range = std::move(R);
210  D.InsideMainFile = true;
211  // Update message to mention original file.
212  D.Message = llvm::formatv("{0}: {1}", Prefix, D.Message);
213  return true;
214 }
215 
216 bool isInsideMainFile(const clang::Diagnostic &D) {
217  if (!D.hasSourceManager())
218  return false;
219 
220  return clangd::isInsideMainFile(D.getLocation(), D.getSourceManager());
221 }
222 
223 bool isNote(DiagnosticsEngine::Level L) {
224  return L == DiagnosticsEngine::Note || L == DiagnosticsEngine::Remark;
225 }
226 
227 llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
228  switch (Lvl) {
229  case DiagnosticsEngine::Ignored:
230  return "ignored";
231  case DiagnosticsEngine::Note:
232  return "note";
233  case DiagnosticsEngine::Remark:
234  return "remark";
236  return "warning";
238  return "error";
239  case DiagnosticsEngine::Fatal:
240  return "fatal error";
241  }
242  llvm_unreachable("unhandled DiagnosticsEngine::Level");
243 }
244 
245 /// Prints a single diagnostic in a clang-like manner, the output includes
246 /// location, severity and error message. An example of the output message is:
247 ///
248 /// main.cpp:12:23: error: undeclared identifier
249 ///
250 /// For main file we only print the basename and for all other files we print
251 /// the filename on a separate line to provide a slightly more readable output
252 /// in the editors:
253 ///
254 /// dir1/dir2/dir3/../../dir4/header.h:12:23
255 /// error: undeclared identifier
256 void printDiag(llvm::raw_string_ostream &OS, const DiagBase &D) {
257  if (D.InsideMainFile) {
258  // Paths to main files are often taken from compile_command.json, where they
259  // are typically absolute. To reduce noise we print only basename for them,
260  // it should not be confusing and saves space.
261  OS << llvm::sys::path::filename(D.File) << ":";
262  } else {
263  OS << D.File << ":";
264  }
265  // Note +1 to line and character. clangd::Range is zero-based, but when
266  // printing for users we want one-based indexes.
267  auto Pos = D.Range.start;
268  OS << (Pos.line + 1) << ":" << (Pos.character + 1) << ":";
269  // The non-main-file paths are often too long, putting them on a separate
270  // line improves readability.
271  if (D.InsideMainFile)
272  OS << " ";
273  else
274  OS << "\n";
275  OS << diagLeveltoString(D.Severity) << ": " << D.Message;
276 }
277 
278 /// Capitalizes the first word in the diagnostic's message.
279 std::string capitalize(std::string Message) {
280  if (!Message.empty())
281  Message[0] = llvm::toUpper(Message[0]);
282  return Message;
283 }
284 
285 /// Returns a message sent to LSP for the main diagnostic in \p D.
286 /// This message may include notes, if they're not emitted in some other way.
287 /// Example output:
288 ///
289 /// no matching function for call to 'foo'
290 ///
291 /// main.cpp:3:5: note: candidate function not viable: requires 2 arguments
292 ///
293 /// dir1/dir2/dir3/../../dir4/header.h:12:23
294 /// note: candidate function not viable: requires 3 arguments
295 std::string mainMessage(const Diag &D, const ClangdDiagnosticOptions &Opts) {
296  std::string Result;
297  llvm::raw_string_ostream OS(Result);
298  OS << D.Message;
299  if (Opts.DisplayFixesCount && !D.Fixes.empty())
300  OS << " (" << (D.Fixes.size() > 1 ? "fixes" : "fix") << " available)";
301  // If notes aren't emitted as structured info, add them to the message.
302  if (!Opts.EmitRelatedLocations)
303  for (auto &Note : D.Notes) {
304  OS << "\n\n";
305  printDiag(OS, Note);
306  }
307  OS.flush();
308  return capitalize(std::move(Result));
309 }
310 
311 /// Returns a message sent to LSP for the note of the main diagnostic.
312 std::string noteMessage(const Diag &Main, const DiagBase &Note,
313  const ClangdDiagnosticOptions &Opts) {
314  std::string Result;
315  llvm::raw_string_ostream OS(Result);
316  OS << Note.Message;
317  // If the client doesn't support structured links between the note and the
318  // original diagnostic, then emit the main diagnostic to give context.
319  if (!Opts.EmitRelatedLocations) {
320  OS << "\n\n";
321  printDiag(OS, Main);
322  }
323  OS.flush();
324  return capitalize(std::move(Result));
325 }
326 
327 void setTags(clangd::Diag &D) {
328  static const auto *DeprecatedDiags = new llvm::DenseSet<unsigned>{
329  diag::warn_access_decl_deprecated,
330  diag::warn_atl_uuid_deprecated,
331  diag::warn_deprecated,
332  diag::warn_deprecated_altivec_src_compat,
333  diag::warn_deprecated_comma_subscript,
334  diag::warn_deprecated_compound_assign_volatile,
335  diag::warn_deprecated_copy,
336  diag::warn_deprecated_copy_with_dtor,
337  diag::warn_deprecated_copy_with_user_provided_copy,
338  diag::warn_deprecated_copy_with_user_provided_dtor,
339  diag::warn_deprecated_def,
340  diag::warn_deprecated_increment_decrement_volatile,
341  diag::warn_deprecated_message,
342  diag::warn_deprecated_redundant_constexpr_static_def,
343  diag::warn_deprecated_register,
344  diag::warn_deprecated_simple_assign_volatile,
345  diag::warn_deprecated_string_literal_conversion,
346  diag::warn_deprecated_this_capture,
347  diag::warn_deprecated_volatile_param,
348  diag::warn_deprecated_volatile_return,
349  diag::warn_deprecated_volatile_structured_binding,
350  diag::warn_opencl_attr_deprecated_ignored,
351  diag::warn_property_method_deprecated,
352  diag::warn_vector_mode_deprecated,
353  };
354  static const auto *UnusedDiags = new llvm::DenseSet<unsigned>{
355  diag::warn_opencl_attr_deprecated_ignored,
356  diag::warn_pragma_attribute_unused,
357  diag::warn_unused_but_set_parameter,
358  diag::warn_unused_but_set_variable,
359  diag::warn_unused_comparison,
360  diag::warn_unused_const_variable,
361  diag::warn_unused_exception_param,
362  diag::warn_unused_function,
363  diag::warn_unused_label,
364  diag::warn_unused_lambda_capture,
365  diag::warn_unused_local_typedef,
366  diag::warn_unused_member_function,
367  diag::warn_unused_parameter,
368  diag::warn_unused_private_field,
369  diag::warn_unused_property_backing_ivar,
370  diag::warn_unused_template,
371  diag::warn_unused_variable,
372  };
373  if (DeprecatedDiags->contains(D.ID)) {
374  D.Tags.push_back(DiagnosticTag::Deprecated);
375  } else if (UnusedDiags->contains(D.ID)) {
376  D.Tags.push_back(DiagnosticTag::Unnecessary);
377  }
378  // FIXME: Set tags for tidy-based diagnostics too.
379 }
380 } // namespace
381 
382 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const DiagBase &D) {
383  OS << "[";
384  if (!D.InsideMainFile)
385  OS << D.File << ":";
386  OS << D.Range.start << "-" << D.Range.end << "] ";
387 
388  return OS << D.Message;
389 }
390 
391 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Fix &F) {
392  OS << F.Message << " {";
393  const char *Sep = "";
394  for (const auto &Edit : F.Edits) {
395  OS << Sep << Edit;
396  Sep = ", ";
397  }
398  return OS << "}";
399 }
400 
401 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Diag &D) {
402  OS << static_cast<const DiagBase &>(D);
403  if (!D.Notes.empty()) {
404  OS << ", notes: {";
405  const char *Sep = "";
406  for (auto &Note : D.Notes) {
407  OS << Sep << Note;
408  Sep = ", ";
409  }
410  OS << "}";
411  }
412  if (!D.Fixes.empty()) {
413  OS << ", fixes: {";
414  const char *Sep = "";
415  for (auto &Fix : D.Fixes) {
416  OS << Sep << Fix;
417  Sep = ", ";
418  }
419  OS << "}";
420  }
421  return OS;
422 }
423 
424 CodeAction toCodeAction(const Fix &F, const URIForFile &File) {
426  Action.title = F.Message;
427  Action.kind = std::string(CodeAction::QUICKFIX_KIND);
428  Action.edit.emplace();
429  Action.edit->changes[File.uri()] = {F.Edits.begin(), F.Edits.end()};
430  return Action;
431 }
432 
433 Diag toDiag(const llvm::SMDiagnostic &D, Diag::DiagSource Source) {
434  Diag Result;
435  Result.Message = D.getMessage().str();
436  switch (D.getKind()) {
437  case llvm::SourceMgr::DK_Error:
438  Result.Severity = DiagnosticsEngine::Error;
439  break;
440  case llvm::SourceMgr::DK_Warning:
441  Result.Severity = DiagnosticsEngine::Warning;
442  break;
443  default:
444  break;
445  }
446  Result.Source = Source;
447  Result.AbsFile = D.getFilename().str();
448  Result.InsideMainFile = D.getSourceMgr()->FindBufferContainingLoc(
449  D.getLoc()) == D.getSourceMgr()->getMainFileID();
450  if (D.getRanges().empty())
451  Result.Range = {{D.getLineNo() - 1, D.getColumnNo()},
452  {D.getLineNo() - 1, D.getColumnNo()}};
453  else
454  Result.Range = {{D.getLineNo() - 1, (int)D.getRanges().front().first},
455  {D.getLineNo() - 1, (int)D.getRanges().front().second}};
456  return Result;
457 }
458 
460  const Diag &D, const URIForFile &File, const ClangdDiagnosticOptions &Opts,
461  llvm::function_ref<void(clangd::Diagnostic, llvm::ArrayRef<Fix>)> OutFn) {
462  clangd::Diagnostic Main;
463  Main.severity = getSeverity(D.Severity);
464 
465  // Main diagnostic should always refer to a range inside main file. If a
466  // diagnostic made it so for, it means either itself or one of its notes is
467  // inside main file. It's also possible that there's a fix in the main file,
468  // but we preserve fixes iff primary diagnostic is in the main file.
469  if (D.InsideMainFile) {
470  Main.range = D.Range;
471  } else {
472  auto It =
473  llvm::find_if(D.Notes, [](const Note &N) { return N.InsideMainFile; });
474  assert(It != D.Notes.end() &&
475  "neither the main diagnostic nor notes are inside main file");
476  Main.range = It->Range;
477  }
478 
479  Main.code = D.Name;
480  if (auto URI = getDiagnosticDocURI(D.Source, D.ID, D.Name)) {
481  Main.codeDescription.emplace();
482  Main.codeDescription->href = std::move(*URI);
483  }
484  switch (D.Source) {
485  case Diag::Clang:
486  Main.source = "clang";
487  break;
488  case Diag::ClangTidy:
489  Main.source = "clang-tidy";
490  break;
491  case Diag::Clangd:
492  Main.source = "clangd";
493  break;
494  case Diag::ClangdConfig:
495  Main.source = "clangd-config";
496  break;
497  case Diag::Unknown:
498  break;
499  }
500  if (Opts.EmbedFixesInDiagnostics) {
501  Main.codeActions.emplace();
502  for (const auto &Fix : D.Fixes)
503  Main.codeActions->push_back(toCodeAction(Fix, File));
504  if (Main.codeActions->size() == 1)
505  Main.codeActions->front().isPreferred = true;
506  }
507  if (Opts.SendDiagnosticCategory && !D.Category.empty())
508  Main.category = D.Category;
509 
510  Main.message = mainMessage(D, Opts);
511  if (Opts.EmitRelatedLocations) {
512  Main.relatedInformation.emplace();
513  for (auto &Note : D.Notes) {
514  if (!Note.AbsFile) {
515  vlog("Dropping note from unknown file: {0}", Note);
516  continue;
517  }
519  RelInfo.location.range = Note.Range;
520  RelInfo.location.uri =
521  URIForFile::canonicalize(*Note.AbsFile, File.file());
522  RelInfo.message = noteMessage(D, Note, Opts);
523  Main.relatedInformation->push_back(std::move(RelInfo));
524  }
525  }
526  Main.tags = D.Tags;
527  OutFn(std::move(Main), D.Fixes);
528 
529  // If we didn't emit the notes as relatedLocations, emit separate diagnostics
530  // so the user can find the locations easily.
531  if (!Opts.EmitRelatedLocations)
532  for (auto &Note : D.Notes) {
533  if (!Note.InsideMainFile)
534  continue;
535  clangd::Diagnostic Res;
537  Res.range = Note.Range;
538  Res.message = noteMessage(D, Note, Opts);
539  OutFn(std::move(Res), llvm::ArrayRef<Fix>());
540  }
541 
542  // FIXME: Get rid of the copies here by taking in a mutable clangd::Diag.
543  for (auto &Entry : D.OpaqueData)
544  Main.data.insert({Entry.first, Entry.second});
545 }
546 
547 int getSeverity(DiagnosticsEngine::Level L) {
548  switch (L) {
549  case DiagnosticsEngine::Remark:
550  return 4;
551  case DiagnosticsEngine::Note:
552  return 3;
554  return 2;
555  case DiagnosticsEngine::Fatal:
557  return 1;
558  case DiagnosticsEngine::Ignored:
559  return 0;
560  }
561  llvm_unreachable("Unknown diagnostic level!");
562 }
563 
564 std::vector<Diag> StoreDiags::take(const clang::tidy::ClangTidyContext *Tidy) {
565  // Do not forget to emit a pending diagnostic if there is one.
566  flushLastDiag();
567 
568  // Fill in name/source now that we have all the context needed to map them.
569  for (auto &Diag : Output) {
570  setTags(Diag);
571  if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
572  // Warnings controlled by -Wfoo are better recognized by that name.
573  StringRef Warning = DiagnosticIDs::getWarningOptionForDiag(Diag.ID);
574  if (!Warning.empty()) {
575  Diag.Name = ("-W" + Warning).str();
576  } else {
577  StringRef Name(ClangDiag);
578  // Almost always an error, with a name like err_enum_class_reference.
579  // Drop the err_ prefix for brevity.
580  Name.consume_front("err_");
581  Diag.Name = std::string(Name);
582  }
584  continue;
585  }
586  if (Tidy != nullptr) {
587  std::string TidyDiag = Tidy->getCheckName(Diag.ID);
588  if (!TidyDiag.empty()) {
589  Diag.Name = std::move(TidyDiag);
591  // clang-tidy bakes the name into diagnostic messages. Strip it out.
592  // It would be much nicer to make clang-tidy not do this.
593  auto CleanMessage = [&](std::string &Msg) {
594  StringRef Rest(Msg);
595  if (Rest.consume_back("]") && Rest.consume_back(Diag.Name) &&
596  Rest.consume_back(" ["))
597  Msg.resize(Rest.size());
598  };
599  CleanMessage(Diag.Message);
600  for (auto &Note : Diag.Notes)
601  CleanMessage(Note.Message);
602  for (auto &Fix : Diag.Fixes)
603  CleanMessage(Fix.Message);
604  continue;
605  }
606  }
607  }
608  // Deduplicate clang-tidy diagnostics -- some clang-tidy checks may emit
609  // duplicated messages due to various reasons (e.g. the check doesn't handle
610  // template instantiations well; clang-tidy alias checks).
611  std::set<std::pair<Range, std::string>> SeenDiags;
612  llvm::erase_if(Output, [&](const Diag &D) {
613  return !SeenDiags.emplace(D.Range, D.Message).second;
614  });
615  return std::move(Output);
616 }
617 
618 void StoreDiags::BeginSourceFile(const LangOptions &Opts,
619  const Preprocessor *PP) {
620  LangOpts = Opts;
621  if (PP) {
622  OrigSrcMgr = &PP->getSourceManager();
623  }
624 }
625 
627  flushLastDiag();
628  LangOpts = None;
629  OrigSrcMgr = nullptr;
630 }
631 
632 /// Sanitizes a piece for presenting it in a synthesized fix message. Ensures
633 /// the result is not too large and does not contain newlines.
634 static void writeCodeToFixMessage(llvm::raw_ostream &OS, llvm::StringRef Code) {
635  constexpr unsigned MaxLen = 50;
636  if (Code == "\n") {
637  OS << "\\n";
638  return;
639  }
640  // Only show the first line if there are many.
641  llvm::StringRef R = Code.split('\n').first;
642  // Shorten the message if it's too long.
643  R = R.take_front(MaxLen);
644 
645  OS << R;
646  if (R.size() != Code.size())
647  OS << "…";
648 }
649 
650 /// Fills \p D with all information, except the location-related bits.
651 /// Also note that ID and Name are not part of clangd::DiagBase and should be
652 /// set elsewhere.
653 static void fillNonLocationData(DiagnosticsEngine::Level DiagLevel,
654  const clang::Diagnostic &Info,
655  clangd::DiagBase &D) {
656  llvm::SmallString<64> Message;
657  Info.FormatDiagnostic(Message);
658 
659  D.Message = std::string(Message.str());
660  D.Severity = DiagLevel;
661  D.Category = DiagnosticIDs::getCategoryNameFromID(
662  DiagnosticIDs::getCategoryNumberForDiag(Info.getID()))
663  .str();
664 }
665 
666 void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
667  const clang::Diagnostic &Info) {
668  // If the diagnostic was generated for a different SourceManager, skip it.
669  // This happens when a module is imported and needs to be implicitly built.
670  // The compilation of that module will use the same StoreDiags, but different
671  // SourceManager.
672  if (OrigSrcMgr && Info.hasSourceManager() &&
673  OrigSrcMgr != &Info.getSourceManager()) {
674  IgnoreDiagnostics::log(DiagLevel, Info);
675  return;
676  }
677 
678  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
679  bool OriginallyError =
680  Info.getDiags()->getDiagnosticIDs()->isDefaultMappingAsError(
681  Info.getID());
682 
683  if (Info.getLocation().isInvalid()) {
684  // Handle diagnostics coming from command-line arguments. The source manager
685  // is *not* available at this point, so we cannot use it.
686  if (!OriginallyError) {
687  IgnoreDiagnostics::log(DiagLevel, Info);
688  return; // non-errors add too much noise, do not show them.
689  }
690 
691  flushLastDiag();
692 
693  LastDiag = Diag();
694  LastDiagLoc.reset();
695  LastDiagOriginallyError = OriginallyError;
696  LastDiag->ID = Info.getID();
697  fillNonLocationData(DiagLevel, Info, *LastDiag);
698  LastDiag->InsideMainFile = true;
699  // Put it at the start of the main file, for a lack of a better place.
700  LastDiag->Range.start = Position{0, 0};
701  LastDiag->Range.end = Position{0, 0};
702  return;
703  }
704 
705  if (!LangOpts || !Info.hasSourceManager()) {
706  IgnoreDiagnostics::log(DiagLevel, Info);
707  return;
708  }
709 
710  SourceManager &SM = Info.getSourceManager();
711 
712  auto FillDiagBase = [&](DiagBase &D) {
713  fillNonLocationData(DiagLevel, Info, D);
714 
715  D.InsideMainFile = isInsideMainFile(Info);
716  D.Range = diagnosticRange(Info, *LangOpts);
717  D.File = std::string(SM.getFilename(Info.getLocation()));
718  D.AbsFile = getCanonicalPath(
719  SM.getFileEntryForID(SM.getFileID(Info.getLocation())), SM);
720  D.ID = Info.getID();
721  return D;
722  };
723 
724  auto AddFix = [&](bool SyntheticMessage) -> bool {
725  assert(!Info.getFixItHints().empty() &&
726  "diagnostic does not have attached fix-its");
727  // No point in generating fixes, if the diagnostic is for a different file.
728  if (!LastDiag->InsideMainFile)
729  return false;
730  // Copy as we may modify the ranges.
731  auto FixIts = Info.getFixItHints().vec();
732  llvm::SmallVector<TextEdit, 1> Edits;
733  for (auto &FixIt : FixIts) {
734  // Allow fixits within a single macro-arg expansion to be applied.
735  // This can be incorrect if the argument is expanded multiple times in
736  // different contexts. Hopefully this is rare!
737  if (FixIt.RemoveRange.getBegin().isMacroID() &&
738  FixIt.RemoveRange.getEnd().isMacroID() &&
739  SM.getFileID(FixIt.RemoveRange.getBegin()) ==
740  SM.getFileID(FixIt.RemoveRange.getEnd())) {
741  FixIt.RemoveRange = CharSourceRange(
742  {SM.getTopMacroCallerLoc(FixIt.RemoveRange.getBegin()),
743  SM.getTopMacroCallerLoc(FixIt.RemoveRange.getEnd())},
744  FixIt.RemoveRange.isTokenRange());
745  }
746  // Otherwise, follow clang's behavior: no fixits in macros.
747  if (FixIt.RemoveRange.getBegin().isMacroID() ||
748  FixIt.RemoveRange.getEnd().isMacroID())
749  return false;
750  if (!isInsideMainFile(FixIt.RemoveRange.getBegin(), SM))
751  return false;
752  Edits.push_back(toTextEdit(FixIt, SM, *LangOpts));
753  }
754 
755  llvm::SmallString<64> Message;
756  // If requested and possible, create a message like "change 'foo' to 'bar'".
757  if (SyntheticMessage && FixIts.size() == 1) {
758  const auto &FixIt = FixIts.front();
759  bool Invalid = false;
760  llvm::StringRef Remove =
761  Lexer::getSourceText(FixIt.RemoveRange, SM, *LangOpts, &Invalid);
762  llvm::StringRef Insert = FixIt.CodeToInsert;
763  if (!Invalid) {
764  llvm::raw_svector_ostream M(Message);
765  if (!Remove.empty() && !Insert.empty()) {
766  M << "change '";
767  writeCodeToFixMessage(M, Remove);
768  M << "' to '";
769  writeCodeToFixMessage(M, Insert);
770  M << "'";
771  } else if (!Remove.empty()) {
772  M << "remove '";
773  writeCodeToFixMessage(M, Remove);
774  M << "'";
775  } else if (!Insert.empty()) {
776  M << "insert '";
777  writeCodeToFixMessage(M, Insert);
778  M << "'";
779  }
780  // Don't allow source code to inject newlines into diagnostics.
781  std::replace(Message.begin(), Message.end(), '\n', ' ');
782  }
783  }
784  if (Message.empty()) // either !SyntheticMessage, or we failed to make one.
785  Info.FormatDiagnostic(Message);
786  LastDiag->Fixes.push_back(
787  Fix{std::string(Message.str()), std::move(Edits)});
788  return true;
789  };
790 
791  if (!isNote(DiagLevel)) {
792  // Handle the new main diagnostic.
793  flushLastDiag();
794 
795  LastDiag = Diag();
796  // FIXME: Merge with feature modules.
797  if (Adjuster)
798  DiagLevel = Adjuster(DiagLevel, Info);
799 
800  FillDiagBase(*LastDiag);
801  if (isExcluded(LastDiag->ID))
802  LastDiag->Severity = DiagnosticsEngine::Ignored;
803  if (DiagCB)
804  DiagCB(Info, *LastDiag);
805  // Don't bother filling in the rest if diag is going to be dropped.
806  if (LastDiag->Severity == DiagnosticsEngine::Ignored)
807  return;
808 
809  LastDiagLoc.emplace(Info.getLocation(), Info.getSourceManager());
810  LastDiagOriginallyError = OriginallyError;
811  if (!Info.getFixItHints().empty())
812  AddFix(true /* try to invent a message instead of repeating the diag */);
813  if (Fixer) {
814  auto ExtraFixes = Fixer(LastDiag->Severity, Info);
815  LastDiag->Fixes.insert(LastDiag->Fixes.end(), ExtraFixes.begin(),
816  ExtraFixes.end());
817  }
818  } else {
819  // Handle a note to an existing diagnostic.
820  if (!LastDiag) {
821  assert(false && "Adding a note without main diagnostic");
822  IgnoreDiagnostics::log(DiagLevel, Info);
823  return;
824  }
825 
826  // If a diagnostic was suppressed due to the suppression filter,
827  // also suppress notes associated with it.
828  if (LastDiag->Severity == DiagnosticsEngine::Ignored)
829  return;
830 
831  // Give include-fixer a chance to replace a note with a fix.
832  if (Fixer) {
833  auto ReplacementFixes = Fixer(LastDiag->Severity, Info);
834  if (!ReplacementFixes.empty()) {
835  assert(Info.getNumFixItHints() == 0 &&
836  "Include-fixer replaced a note with clang fix-its attached!");
837  LastDiag->Fixes.insert(LastDiag->Fixes.end(), ReplacementFixes.begin(),
838  ReplacementFixes.end());
839  return;
840  }
841  }
842 
843  if (!Info.getFixItHints().empty()) {
844  // A clang note with fix-it is not a separate diagnostic in clangd. We
845  // attach it as a Fix to the main diagnostic instead.
846  if (!AddFix(false /* use the note as the message */))
847  IgnoreDiagnostics::log(DiagLevel, Info);
848  } else {
849  // A clang note without fix-its corresponds to clangd::Note.
850  Note N;
851  FillDiagBase(N);
852 
853  LastDiag->Notes.push_back(std::move(N));
854  }
855  }
856 }
857 
858 void StoreDiags::flushLastDiag() {
859  if (!LastDiag)
860  return;
861  auto Finish = llvm::make_scope_exit([&, NDiags(Output.size())] {
862  if (Output.size() == NDiags) // No new diag emitted.
863  vlog("Dropped diagnostic: {0}: {1}", LastDiag->File, LastDiag->Message);
864  LastDiag.reset();
865  });
866 
867  if (LastDiag->Severity == DiagnosticsEngine::Ignored)
868  return;
869  // Move errors that occur from headers into main file.
870  if (!LastDiag->InsideMainFile && LastDiagLoc && LastDiagOriginallyError) {
871  if (tryMoveToMainFile(*LastDiag, *LastDiagLoc)) {
872  // Suppress multiple errors from the same inclusion.
873  if (!IncludedErrorLocations
874  .insert({LastDiag->Range.start.line,
875  LastDiag->Range.start.character})
876  .second)
877  return;
878  }
879  }
880  if (!mentionsMainFile(*LastDiag))
881  return;
882  Output.push_back(std::move(*LastDiag));
883 }
884 
886  const llvm::StringSet<> &Suppress,
887  const LangOptions &LangOpts) {
888  // Don't complain about header-only stuff in mainfiles if it's a header.
889  // FIXME: would be cleaner to suppress in clang, once we decide whether the
890  // behavior should be to silently-ignore or respect the pragma.
891  if (ID == diag::pp_pragma_sysheader_in_main_file && LangOpts.IsHeaderFile)
892  return true;
893 
894  if (const char *CodePtr = getDiagnosticCode(ID)) {
895  if (Suppress.contains(normalizeSuppressedCode(CodePtr)))
896  return true;
897  }
898  StringRef Warning = DiagnosticIDs::getWarningOptionForDiag(ID);
899  if (!Warning.empty() && Suppress.contains(Warning))
900  return true;
901  return false;
902 }
903 
904 llvm::StringRef normalizeSuppressedCode(llvm::StringRef Code) {
905  Code.consume_front("err_");
906  Code.consume_front("-W");
907  return Code;
908 }
909 
910 llvm::Optional<std::string> getDiagnosticDocURI(Diag::DiagSource Source,
911  unsigned ID,
912  llvm::StringRef Name) {
913  switch (Source) {
914  case Diag::Unknown:
915  break;
916  case Diag::Clang:
917  // There is a page listing many warning flags, but it provides too little
918  // information to be worth linking.
919  // https://clang.llvm.org/docs/DiagnosticsReference.html
920  break;
921  case Diag::ClangTidy: {
922  StringRef Module, Check;
923  // This won't correctly get the module for clang-analyzer checks, but as we
924  // don't link in the analyzer that shouldn't be an issue.
925  // This would also need updating if anyone decides to create a module with a
926  // '-' in the name.
927  std::tie(Module, Check) = Name.split('-');
928  if (Module.empty() || Check.empty())
929  return llvm::None;
930  return ("https://clang.llvm.org/extra/clang-tidy/checks/" + Module + "/" +
931  Check + ".html")
932  .str();
933  }
934  case Diag::Clangd:
935  if (Name == "unused-includes")
936  return {"https://clangd.llvm.org/guides/include-cleaner"};
937  break;
938  case Diag::ClangdConfig:
939  // FIXME: we should link to https://clangd.llvm.org/config
940  // However we have no diagnostic codes, which the link should describe!
941  break;
942  }
943  return llvm::None;
944 }
945 
946 } // namespace clangd
947 } // namespace clang
clang::clangd::DiagBase::Severity
DiagnosticsEngine::Level Severity
Definition: Diagnostics.h:67
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:39
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:45
clang::clangd::MessageType::Warning
@ Warning
A warning message.
clang::clangd::Diag::ClangTidy
@ ClangTidy
Definition: Diagnostics.h:100
clang::clangd::Diagnostic::source
std::string source
A human-readable string describing the source of this diagnostic, e.g.
Definition: Protocol.h:871
clang::clangd::Edit
A set of edits generated for a single file.
Definition: SourceCode.h:184
clang::clangd::Location::uri
URIForFile uri
The text document's URI.
Definition: Protocol.h:213
clang::clangd::Fix
Represents a single fix-it that editor can apply to fix the error.
Definition: Diagnostics.h:81
clang::clangd::CodeAction
A code action represents a change that can be performed in code, e.g.
Definition: Protocol.h:1001
clang::clangd::Diagnostic::category
llvm::Optional< std::string > category
The diagnostic's category.
Definition: Protocol.h:887
clang::clangd::Diag::Name
std::string Name
Definition: Diagnostics.h:95
clang::clangd::Diagnostic::codeDescription
llvm::Optional< CodeDescription > codeDescription
An optional property to describe the error code.
Definition: Protocol.h:867
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:276
clang::tidy::bugprone::Message
static const char Message[]
Definition: ReservedIdentifierCheck.cpp:31
clang::clangd::DiagBase::InsideMainFile
bool InsideMainFile
Definition: Diagnostics.h:71
clang::tidy::cppcoreguidelines::getSourceText
static std::string getSourceText(const CXXDestructorDecl &Destructor)
Definition: VirtualClassDestructorCheck.cpp:115
clang::clangd::toTextEdit
TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, const LangOptions &L)
Definition: SourceCode.cpp:554
clang::clangd::ClangdDiagnosticOptions
Definition: Diagnostics.h:36
clang::clangd::getSeverity
int getSeverity(DiagnosticsEngine::Level L)
Convert from clang diagnostic level to LSP severity.
Definition: Diagnostics.cpp:547
clang::clangd::Location::range
Range range
Definition: Protocol.h:214
clang::clangd::Diag::Source
enum clang::clangd::Diag::DiagSource 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::StoreDiags::HandleDiagnostic
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override
Definition: Diagnostics.cpp:666
clang::clangd::sourceLocToPosition
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
Definition: SourceCode.cpp:213
clang::clangd::Diagnostic::range
Range range
The range at which the message applies.
Definition: Protocol.h:857
Pos
size_t Pos
Definition: NoLintDirectiveHandler.cpp:97
clang::clangd::DiagnosticRelatedInformation::location
Location location
The location of this related diagnostic information.
Definition: Protocol.h:828
FixIt
llvm::Optional< FixItHint > FixIt
Definition: UppercaseLiteralSuffixCheck.cpp:63
Protocol.h
M
const google::protobuf::Message & M
Definition: Server.cpp:309
Error
constexpr static llvm::SourceMgr::DiagKind Error
Definition: ConfigCompile.cpp:591
clang::clangd::Diagnostic
Definition: Protocol.h:855
clang::clangd::halfOpenToRange
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R)
Definition: SourceCode.cpp:467
clang::clangd::Position
Definition: Protocol.h:156
Code
std::string Code
Definition: FindTargetTests.cpp:67
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
Diagnostic
DiagnosticCallback Diagnostic
Definition: ConfigCompile.cpp:100
clang::clangd::DiagBase::Message
std::string Message
Definition: Diagnostics.h:59
clang::clangd::Diagnostic::tags
llvm::SmallVector< DiagnosticTag, 1 > tags
Additional metadata about the diagnostic.
Definition: Protocol.h:877
clang::clangd::Fix::Message
std::string Message
Message for the fix-it.
Definition: Diagnostics.h:83
clang::clangd::getCanonicalPath
llvm::Optional< std::string > getCanonicalPath(const FileEntry *F, const SourceManager &SourceMgr)
Get the canonical path of F.
Definition: SourceCode.cpp:515
clang::clangd::StoreDiags::EndSourceFile
void EndSourceFile() override
Definition: Diagnostics.cpp:626
clang::clangd::Diag
A top-level diagnostic that may have Notes and Fixes.
Definition: Diagnostics.h:94
Logger.h
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:67
clang::clangd::Diag::Notes
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
Definition: Diagnostics.h:105
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:653
clang::clangd::Diagnostic::data
llvm::json::Object data
A data entry field that is preserved between a textDocument/publishDiagnostics notification and textD...
Definition: Protocol.h:899
clang::clangd::toDiag
Diag toDiag(const llvm::SMDiagnostic &D, Diag::DiagSource Source)
Definition: Diagnostics.cpp:433
clang::clangd::Diag::Unknown
@ Unknown
Definition: Diagnostics.h:98
clang::clangd::replace
static std::string replace(llvm::StringRef Haystack, llvm::StringRef Needle, llvm::StringRef Repl)
Definition: TestIndex.cpp:30
clang::clangd::vlog
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:72
Diagnostics.h
clang::clangd::Diag::Fixes
std::vector< Fix > Fixes
Alternative fixes for this diagnostic, one should be chosen.
Definition: Diagnostics.h:107
Name
Token Name
Definition: MacroToEnumCheck.cpp:89
clang::clangd::URIForFile
Definition: Protocol.h:84
clang::clangd::Diag::Clang
@ Clang
Definition: Diagnostics.h:99
clang::clangd::operator<<
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
Definition: CodeComplete.cpp:2217
Output
std::string Output
Definition: TraceTests.cpp:159
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
clang::clangd::DiagBase::AbsFile
llvm::Optional< std::string > AbsFile
Definition: Diagnostics.h:64
clang::clangd::Deprecated
@ Deprecated
Deprecated or obsolete code.
Definition: Protocol.h:843
clang::clangd::toCodeAction
CodeAction toCodeAction(const Fix &F, const URIForFile &File)
Convert from Fix to LSP CodeAction.
Definition: Diagnostics.cpp:424
Entry
Definition: Modularize.cpp:427
SourceCode.h
Compiler.h
Info
FunctionInfo Info
Definition: FunctionSizeCheck.cpp:121
ID
static char ID
Definition: Logger.cpp:74
clang::clangd::Diagnostic::message
std::string message
The diagnostic's message.
Definition: Protocol.h:874
clang::clangd::URIForFile::canonicalize
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
Definition: Protocol.cpp:45
clang::clangd::Diag::Clangd
@ Clangd
Definition: Diagnostics.h:101
clang::clangd::Diagnostic::severity
int severity
The diagnostic's severity.
Definition: Protocol.h:861
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:459
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::Diagnostic::codeActions
llvm::Optional< std::vector< CodeAction > > codeActions
Clangd extension: code actions related to this diagnostic.
Definition: Protocol.h:892
OS
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:160
clang::clangd::isInsideMainFile
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
Definition: SourceCode.cpp:418
clang::clangd::Diag::ClangdConfig
@ ClangdConfig
Definition: Diagnostics.h:102
clang::clangd::DiagBase::ID
unsigned ID
Definition: Diagnostics.h:72
LangOpts
const LangOptions * LangOpts
Definition: ExtractFunction.cpp:375
clang::clangd::normalizeSuppressedCode
llvm::StringRef normalizeSuppressedCode(llvm::StringRef Code)
Take a user-specified diagnostic code, and convert it to a normalized form stored in the config and c...
Definition: Diagnostics.cpp:904
clang::clangd::Diagnostic::code
std::string code
The diagnostic's code. Can be omitted.
Definition: Protocol.h:864
clang::clangd::Note
Represents a note for the diagnostic.
Definition: Diagnostics.h:91
clang::clangd::Diag::DiagSource
DiagSource
Definition: Diagnostics.h:97
clang::clangd::getDiagnosticDocURI
llvm::Optional< std::string > getDiagnosticDocURI(Diag::DiagSource Source, unsigned ID, llvm::StringRef Name)
Returns a URI providing more information about a particular diagnostic.
Definition: Diagnostics.cpp:910
clang::clangd::CompletionItemKind::Module
@ Module
clang::clangd::IgnoreDiagnostics::log
static void log(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info)
Definition: Compiler.cpp:20
clang::clangd::Diagnostic::relatedInformation
llvm::Optional< std::vector< DiagnosticRelatedInformation > > relatedInformation
An array of related diagnostic information, e.g.
Definition: Protocol.h:881
clang::clangd::Unnecessary
@ Unnecessary
Unused or unnecessary code.
Definition: Protocol.h:839
clang::clangd::DiagnosticRelatedInformation
Represents a related message and source code location for a diagnostic.
Definition: Protocol.h:826
clang::clangd::DiagBase
Contains basic information about a diagnostic.
Definition: Diagnostics.h:58
Warning
constexpr static llvm::SourceMgr::DiagKind Warning
Definition: ConfigCompile.cpp:592
clang::clangd::StoreDiags::BeginSourceFile
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *PP) override
Definition: Diagnostics.cpp:618
clang::clangd::isBuiltinDiagnosticSuppressed
bool isBuiltinDiagnosticSuppressed(unsigned ID, const llvm::StringSet<> &Suppress, const LangOptions &LangOpts)
Determine whether a (non-clang-tidy) diagnostic is suppressed by config.
Definition: Diagnostics.cpp:885
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:634
clang::clangd::StoreDiags::take
std::vector< Diag > take(const clang::tidy::ClangTidyContext *Tidy=nullptr)
Definition: Diagnostics.cpp:564
clang::clangd::URI
A URI describes the location of a source file.
Definition: URI.h:28
clang::clangd::DiagnosticRelatedInformation::message
std::string message
The message of this related diagnostic information.
Definition: Protocol.h:830
clang::clangd::CodeAction::QUICKFIX_KIND
const static llvm::StringLiteral QUICKFIX_KIND
Definition: Protocol.h:1008
clang::clangd::DiagBase::Range
clangd::Range Range
Definition: Diagnostics.h:66
Action
FieldAction Action
Definition: MemberwiseConstructor.cpp:261