clang-tools  8.0.0
ClangTidyDiagnosticConsumer.cpp
Go to the documentation of this file.
1 //===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
11 /// and ClangTidyError classes.
12 ///
13 /// This tool uses the Clang Tooling infrastructure, see
14 /// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
15 /// for details on setting it up with LLVM source tree.
16 ///
17 //===----------------------------------------------------------------------===//
18 
20 #include "ClangTidyOptions.h"
21 #include "clang/AST/ASTDiagnostic.h"
22 #include "clang/Basic/DiagnosticOptions.h"
23 #include "clang/Frontend/DiagnosticRenderer.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include "llvm/ADT/SmallString.h"
26 #include <tuple>
27 #include <vector>
28 using namespace clang;
29 using namespace tidy;
30 
31 namespace {
32 class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {
33 public:
34  ClangTidyDiagnosticRenderer(const LangOptions &LangOpts,
35  DiagnosticOptions *DiagOpts,
36  ClangTidyError &Error)
37  : DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
38 
39 protected:
40  void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
41  DiagnosticsEngine::Level Level, StringRef Message,
42  ArrayRef<CharSourceRange> Ranges,
43  DiagOrStoredDiag Info) override {
44  // Remove check name from the message.
45  // FIXME: Remove this once there's a better way to pass check names than
46  // appending the check name to the message in ClangTidyContext::diag and
47  // using getCustomDiagID.
48  std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";
49  if (Message.endswith(CheckNameInMessage))
50  Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
51 
52  auto TidyMessage =
53  Loc.isValid()
54  ? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
55  : tooling::DiagnosticMessage(Message);
56  if (Level == DiagnosticsEngine::Note) {
57  Error.Notes.push_back(TidyMessage);
58  return;
59  }
60  assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
61  Error.Message = TidyMessage;
62  }
63 
64  void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
65  DiagnosticsEngine::Level Level,
66  ArrayRef<CharSourceRange> Ranges) override {}
67 
68  void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
69  SmallVectorImpl<CharSourceRange> &Ranges,
70  ArrayRef<FixItHint> Hints) override {
71  assert(Loc.isValid());
72  for (const auto &FixIt : Hints) {
73  CharSourceRange Range = FixIt.RemoveRange;
74  assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
75  "Invalid range in the fix-it hint.");
76  assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
77  "Only file locations supported in fix-it hints.");
78 
79  tooling::Replacement Replacement(Loc.getManager(), Range,
80  FixIt.CodeToInsert);
81  llvm::Error Err = Error.Fix[Replacement.getFilePath()].add(Replacement);
82  // FIXME: better error handling (at least, don't let other replacements be
83  // applied).
84  if (Err) {
85  llvm::errs() << "Fix conflicts with existing fix! "
86  << llvm::toString(std::move(Err)) << "\n";
87  assert(false && "Fix conflicts with existing fix!");
88  }
89  }
90  }
91 
92  void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override {}
93 
94  void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
95  StringRef ModuleName) override {}
96 
97  void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
98  StringRef ModuleName) override {}
99 
100  void endDiagnostic(DiagOrStoredDiag D,
101  DiagnosticsEngine::Level Level) override {
102  assert(!Error.Message.Message.empty() && "Message has not been set");
103  }
104 
105 private:
106  ClangTidyError &Error;
107 };
108 } // end anonymous namespace
109 
110 ClangTidyError::ClangTidyError(StringRef CheckName,
111  ClangTidyError::Level DiagLevel,
112  StringRef BuildDirectory, bool IsWarningAsError)
113  : tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
114  IsWarningAsError(IsWarningAsError) {}
115 
116 // Returns true if GlobList starts with the negative indicator ('-'), removes it
117 // from the GlobList.
118 static bool ConsumeNegativeIndicator(StringRef &GlobList) {
119  GlobList = GlobList.trim(" \r\n");
120  if (GlobList.startswith("-")) {
121  GlobList = GlobList.substr(1);
122  return true;
123  }
124  return false;
125 }
126 // Converts first glob from the comma-separated list of globs to Regex and
127 // removes it and the trailing comma from the GlobList.
128 static llvm::Regex ConsumeGlob(StringRef &GlobList) {
129  StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find(','));
130  StringRef Glob = UntrimmedGlob.trim(' ');
131  GlobList = GlobList.substr(UntrimmedGlob.size() + 1);
132  SmallString<128> RegexText("^");
133  StringRef MetaChars("()^$|*+?.[]\\{}");
134  for (char C : Glob) {
135  if (C == '*')
136  RegexText.push_back('.');
137  else if (MetaChars.find(C) != StringRef::npos)
138  RegexText.push_back('\\');
139  RegexText.push_back(C);
140  }
141  RegexText.push_back('$');
142  return llvm::Regex(RegexText);
143 }
144 
145 GlobList::GlobList(StringRef Globs)
146  : Positive(!ConsumeNegativeIndicator(Globs)), Regex(ConsumeGlob(Globs)),
147  NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}
148 
149 bool GlobList::contains(StringRef S, bool Contains) {
150  if (Regex.match(S))
151  Contains = Positive;
152 
153  if (NextGlob)
154  Contains = NextGlob->contains(S, Contains);
155  return Contains;
156 }
157 
159 public:
160  CachedGlobList(StringRef Globs) : Globs(Globs) {}
161 
162  bool contains(StringRef S) {
163  switch (auto &Result = Cache[S]) {
164  case Yes: return true;
165  case No: return false;
166  case None:
167  Result = Globs.contains(S) ? Yes : No;
168  return Result == Yes;
169  }
170  llvm_unreachable("invalid enum");
171  }
172 
173 private:
174  GlobList Globs;
175  enum Tristate { None, Yes, No };
176  llvm::StringMap<Tristate> Cache;
177 };
178 
180  std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
182  : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
183  Profile(false),
184  AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers) {
185  // Before the first translation unit we can get errors related to command-line
186  // parsing, use empty string for the file name in this case.
187  setCurrentFile("");
188 }
189 
191 
192 DiagnosticBuilder ClangTidyContext::diag(
193  StringRef CheckName, SourceLocation Loc, StringRef Description,
194  DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
195  assert(Loc.isValid());
196  unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
197  Level, (Description + " [" + CheckName + "]").str());
198  CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
199  return DiagEngine->Report(Loc, ID);
200 }
201 
202 void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
203  DiagEngine->setSourceManager(SourceMgr);
204 }
205 
206 void ClangTidyContext::setCurrentFile(StringRef File) {
207  CurrentFile = File;
208  CurrentOptions = getOptionsForFile(CurrentFile);
209  CheckFilter = llvm::make_unique<CachedGlobList>(*getOptions().Checks);
210  WarningAsErrorFilter =
211  llvm::make_unique<CachedGlobList>(*getOptions().WarningsAsErrors);
212 }
213 
214 void ClangTidyContext::setASTContext(ASTContext *Context) {
215  DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
216  LangOpts = Context->getLangOpts();
217 }
218 
220  return OptionsProvider->getGlobalOptions();
221 }
222 
224  return CurrentOptions;
225 }
226 
228  // Merge options on top of getDefaults() as a safeguard against options with
229  // unset values.
231  OptionsProvider->getOptions(File));
232 }
233 
234 void ClangTidyContext::setEnableProfiling(bool P) { Profile = P; }
235 
237  ProfilePrefix = Prefix;
238 }
239 
240 llvm::Optional<ClangTidyProfiling::StorageParams>
242  if (ProfilePrefix.empty())
243  return llvm::None;
244 
245  return ClangTidyProfiling::StorageParams(ProfilePrefix, CurrentFile);
246 }
247 
248 bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const {
249  assert(CheckFilter != nullptr);
250  return CheckFilter->contains(CheckName);
251 }
252 
253 bool ClangTidyContext::treatAsError(StringRef CheckName) const {
254  assert(WarningAsErrorFilter != nullptr);
255  return WarningAsErrorFilter->contains(CheckName);
256 }
257 
258 StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
259  llvm::DenseMap<unsigned, std::string>::const_iterator I =
260  CheckNamesByDiagnosticID.find(DiagnosticID);
261  if (I != CheckNamesByDiagnosticID.end())
262  return I->second;
263  return "";
264 }
265 
267  ClangTidyContext &Ctx, bool RemoveIncompatibleErrors)
268  : Context(Ctx), RemoveIncompatibleErrors(RemoveIncompatibleErrors),
269  LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false),
270  LastErrorWasIgnored(false) {}
271 
272 void ClangTidyDiagnosticConsumer::finalizeLastError() {
273  if (!Errors.empty()) {
274  ClangTidyError &Error = Errors.back();
275  if (!Context.isCheckEnabled(Error.DiagnosticName) &&
276  Error.DiagLevel != ClangTidyError::Error) {
277  ++Context.Stats.ErrorsIgnoredCheckFilter;
278  Errors.pop_back();
279  } else if (!LastErrorRelatesToUserCode) {
280  ++Context.Stats.ErrorsIgnoredNonUserCode;
281  Errors.pop_back();
282  } else if (!LastErrorPassesLineFilter) {
283  ++Context.Stats.ErrorsIgnoredLineFilter;
284  Errors.pop_back();
285  } else {
286  ++Context.Stats.ErrorsDisplayed;
287  }
288  }
289  LastErrorRelatesToUserCode = false;
290  LastErrorPassesLineFilter = false;
291 }
292 
293 static bool IsNOLINTFound(StringRef NolintDirectiveText, StringRef Line,
294  unsigned DiagID, const ClangTidyContext &Context) {
295  const size_t NolintIndex = Line.find(NolintDirectiveText);
296  if (NolintIndex == StringRef::npos)
297  return false;
298 
299  size_t BracketIndex = NolintIndex + NolintDirectiveText.size();
300  // Check if the specific checks are specified in brackets.
301  if (BracketIndex < Line.size() && Line[BracketIndex] == '(') {
302  ++BracketIndex;
303  const size_t BracketEndIndex = Line.find(')', BracketIndex);
304  if (BracketEndIndex != StringRef::npos) {
305  StringRef ChecksStr =
306  Line.substr(BracketIndex, BracketEndIndex - BracketIndex);
307  // Allow disabling all the checks with "*".
308  if (ChecksStr != "*") {
309  StringRef CheckName = Context.getCheckName(DiagID);
310  // Allow specifying a few check names, delimited with comma.
311  SmallVector<StringRef, 1> Checks;
312  ChecksStr.split(Checks, ',', -1, false);
313  llvm::transform(Checks, Checks.begin(),
314  [](StringRef S) { return S.trim(); });
315  return llvm::find(Checks, CheckName) != Checks.end();
316  }
317  }
318  }
319  return true;
320 }
321 
322 static bool LineIsMarkedWithNOLINT(const SourceManager &SM, SourceLocation Loc,
323  unsigned DiagID,
324  const ClangTidyContext &Context) {
325  bool Invalid;
326  const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
327  if (Invalid)
328  return false;
329 
330  // Check if there's a NOLINT on this line.
331  const char *P = CharacterData;
332  while (*P != '\0' && *P != '\r' && *P != '\n')
333  ++P;
334  StringRef RestOfLine(CharacterData, P - CharacterData + 1);
335  if (IsNOLINTFound("NOLINT", RestOfLine, DiagID, Context))
336  return true;
337 
338  // Check if there's a NOLINTNEXTLINE on the previous line.
339  const char *BufBegin =
340  SM.getCharacterData(SM.getLocForStartOfFile(SM.getFileID(Loc)), &Invalid);
341  if (Invalid || P == BufBegin)
342  return false;
343 
344  // Scan backwards over the current line.
345  P = CharacterData;
346  while (P != BufBegin && *P != '\n')
347  --P;
348 
349  // If we reached the begin of the file there is no line before it.
350  if (P == BufBegin)
351  return false;
352 
353  // Skip over the newline.
354  --P;
355  const char *LineEnd = P;
356 
357  // Now we're on the previous line. Skip to the beginning of it.
358  while (P != BufBegin && *P != '\n')
359  --P;
360 
361  RestOfLine = StringRef(P, LineEnd - P + 1);
362  if (IsNOLINTFound("NOLINTNEXTLINE", RestOfLine, DiagID, Context))
363  return true;
364 
365  return false;
366 }
367 
368 static bool LineIsMarkedWithNOLINTinMacro(const SourceManager &SM,
369  SourceLocation Loc, unsigned DiagID,
370  const ClangTidyContext &Context) {
371  while (true) {
372  if (LineIsMarkedWithNOLINT(SM, Loc, DiagID, Context))
373  return true;
374  if (!Loc.isMacroID())
375  return false;
376  Loc = SM.getImmediateExpansionRange(Loc).getBegin();
377  }
378  return false;
379 }
380 
382  DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
383  if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
384  return;
385 
386  if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&
387  DiagLevel != DiagnosticsEngine::Fatal &&
388  LineIsMarkedWithNOLINTinMacro(Info.getSourceManager(),
389  Info.getLocation(), Info.getID(),
390  Context)) {
391  ++Context.Stats.ErrorsIgnoredNOLINT;
392  // Ignored a warning, should ignore related notes as well
393  LastErrorWasIgnored = true;
394  return;
395  }
396 
397  LastErrorWasIgnored = false;
398  // Count warnings/errors.
399  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
400 
401  if (DiagLevel == DiagnosticsEngine::Note) {
402  assert(!Errors.empty() &&
403  "A diagnostic note can only be appended to a message.");
404  } else {
405  finalizeLastError();
406  StringRef WarningOption =
407  Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(
408  Info.getID());
409  std::string CheckName = !WarningOption.empty()
410  ? ("clang-diagnostic-" + WarningOption).str()
411  : Context.getCheckName(Info.getID()).str();
412 
413  if (CheckName.empty()) {
414  // This is a compiler diagnostic without a warning option. Assign check
415  // name based on its level.
416  switch (DiagLevel) {
417  case DiagnosticsEngine::Error:
418  case DiagnosticsEngine::Fatal:
419  CheckName = "clang-diagnostic-error";
420  break;
421  case DiagnosticsEngine::Warning:
422  CheckName = "clang-diagnostic-warning";
423  break;
424  default:
425  CheckName = "clang-diagnostic-unknown";
426  break;
427  }
428  }
429 
430  ClangTidyError::Level Level = ClangTidyError::Warning;
431  if (DiagLevel == DiagnosticsEngine::Error ||
432  DiagLevel == DiagnosticsEngine::Fatal) {
433  // Force reporting of Clang errors regardless of filters and non-user
434  // code.
435  Level = ClangTidyError::Error;
436  LastErrorRelatesToUserCode = true;
437  LastErrorPassesLineFilter = true;
438  }
439  bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
440  Context.treatAsError(CheckName);
441  Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
442  IsWarningAsError);
443  }
444 
445  ClangTidyDiagnosticRenderer Converter(
446  Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
447  Errors.back());
448  SmallString<100> Message;
449  Info.FormatDiagnostic(Message);
450  FullSourceLoc Loc;
451  if (Info.getLocation().isValid() && Info.hasSourceManager())
452  Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
453  Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
454  Info.getFixItHints());
455 
456  if (Info.hasSourceManager())
457  checkFilters(Info.getLocation(), Info.getSourceManager());
458 }
459 
460 bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
461  unsigned LineNumber) const {
462  if (Context.getGlobalOptions().LineFilter.empty())
463  return true;
464  for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
465  if (FileName.endswith(Filter.Name)) {
466  if (Filter.LineRanges.empty())
467  return true;
468  for (const FileFilter::LineRange &Range : Filter.LineRanges) {
469  if (Range.first <= LineNumber && LineNumber <= Range.second)
470  return true;
471  }
472  return false;
473  }
474  }
475  return false;
476 }
477 
478 void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
479  const SourceManager &Sources) {
480  // Invalid location may mean a diagnostic in a command line, don't skip these.
481  if (!Location.isValid()) {
482  LastErrorRelatesToUserCode = true;
483  LastErrorPassesLineFilter = true;
484  return;
485  }
486 
487  if (!*Context.getOptions().SystemHeaders &&
488  Sources.isInSystemHeader(Location))
489  return;
490 
491  // FIXME: We start with a conservative approach here, but the actual type of
492  // location needed depends on the check (in particular, where this check wants
493  // to apply fixes).
494  FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
495  const FileEntry *File = Sources.getFileEntryForID(FID);
496 
497  // -DMACRO definitions on the command line have locations in a virtual buffer
498  // that doesn't have a FileEntry. Don't skip these as well.
499  if (!File) {
500  LastErrorRelatesToUserCode = true;
501  LastErrorPassesLineFilter = true;
502  return;
503  }
504 
505  StringRef FileName(File->getName());
506  LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
507  Sources.isInMainFile(Location) ||
508  getHeaderFilter()->match(FileName);
509 
510  unsigned LineNumber = Sources.getExpansionLineNumber(Location);
511  LastErrorPassesLineFilter =
512  LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
513 }
514 
515 llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
516  if (!HeaderFilter)
517  HeaderFilter =
518  llvm::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
519  return HeaderFilter.get();
520 }
521 
522 void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
523  // Each error is modelled as the set of intervals in which it applies
524  // replacements. To detect overlapping replacements, we use a sweep line
525  // algorithm over these sets of intervals.
526  // An event here consists of the opening or closing of an interval. During the
527  // process, we maintain a counter with the amount of open intervals. If we
528  // find an endpoint of an interval and this counter is different from 0, it
529  // means that this interval overlaps with another one, so we set it as
530  // inapplicable.
531  struct Event {
532  // An event can be either the begin or the end of an interval.
533  enum EventType {
534  ET_Begin = 1,
535  ET_End = -1,
536  };
537 
538  Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,
539  unsigned ErrorSize)
540  : Type(Type), ErrorId(ErrorId) {
541  // The events are going to be sorted by their position. In case of draw:
542  //
543  // * If an interval ends at the same position at which other interval
544  // begins, this is not an overlapping, so we want to remove the ending
545  // interval before adding the starting one: end events have higher
546  // priority than begin events.
547  //
548  // * If we have several begin points at the same position, we will mark as
549  // inapplicable the ones that we process later, so the first one has to
550  // be the one with the latest end point, because this one will contain
551  // all the other intervals. For the same reason, if we have several end
552  // points in the same position, the last one has to be the one with the
553  // earliest begin point. In both cases, we sort non-increasingly by the
554  // position of the complementary.
555  //
556  // * In case of two equal intervals, the one whose error is bigger can
557  // potentially contain the other one, so we want to process its begin
558  // points before and its end points later.
559  //
560  // * Finally, if we have two equal intervals whose errors have the same
561  // size, none of them will be strictly contained inside the other.
562  // Sorting by ErrorId will guarantee that the begin point of the first
563  // one will be processed before, disallowing the second one, and the
564  // end point of the first one will also be processed before,
565  // disallowing the first one.
566  if (Type == ET_Begin)
567  Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
568  else
569  Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
570  }
571 
572  bool operator<(const Event &Other) const {
573  return Priority < Other.Priority;
574  }
575 
576  // Determines if this event is the begin or the end of an interval.
577  EventType Type;
578  // The index of the error to which the interval that generated this event
579  // belongs.
580  unsigned ErrorId;
581  // The events will be sorted based on this field.
582  std::tuple<unsigned, EventType, int, int, unsigned> Priority;
583  };
584 
585  // Compute error sizes.
586  std::vector<int> Sizes;
587  for (const auto &Error : Errors) {
588  int Size = 0;
589  for (const auto &FileAndReplaces : Error.Fix) {
590  for (const auto &Replace : FileAndReplaces.second)
591  Size += Replace.getLength();
592  }
593  Sizes.push_back(Size);
594  }
595 
596  // Build events from error intervals.
597  std::map<std::string, std::vector<Event>> FileEvents;
598  for (unsigned I = 0; I < Errors.size(); ++I) {
599  for (const auto &FileAndReplace : Errors[I].Fix) {
600  for (const auto &Replace : FileAndReplace.second) {
601  unsigned Begin = Replace.getOffset();
602  unsigned End = Begin + Replace.getLength();
603  const std::string &FilePath = Replace.getFilePath();
604  // FIXME: Handle empty intervals, such as those from insertions.
605  if (Begin == End)
606  continue;
607  auto &Events = FileEvents[FilePath];
608  Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
609  Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
610  }
611  }
612  }
613 
614  std::vector<bool> Apply(Errors.size(), true);
615  for (auto &FileAndEvents : FileEvents) {
616  std::vector<Event> &Events = FileAndEvents.second;
617  // Sweep.
618  std::sort(Events.begin(), Events.end());
619  int OpenIntervals = 0;
620  for (const auto &Event : Events) {
621  if (Event.Type == Event::ET_End)
622  --OpenIntervals;
623  // This has to be checked after removing the interval from the count if it
624  // is an end event, or before adding it if it is a begin event.
625  if (OpenIntervals != 0)
626  Apply[Event.ErrorId] = false;
627  if (Event.Type == Event::ET_Begin)
628  ++OpenIntervals;
629  }
630  assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
631  }
632 
633  for (unsigned I = 0; I < Errors.size(); ++I) {
634  if (!Apply[I]) {
635  Errors[I].Fix.clear();
636  Errors[I].Notes.emplace_back(
637  "this fix will not be applied because it overlaps with another fix");
638  }
639  }
640 }
641 
642 namespace {
643 struct LessClangTidyError {
644  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
645  const tooling::DiagnosticMessage &M1 = LHS.Message;
646  const tooling::DiagnosticMessage &M2 = RHS.Message;
647 
648  return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
649  std::tie(M2.FilePath, M2.FileOffset, M2.Message);
650  }
651 };
652 struct EqualClangTidyError {
653  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
654  LessClangTidyError Less;
655  return !Less(LHS, RHS) && !Less(RHS, LHS);
656  }
657 };
658 } // end anonymous namespace
659 
660 std::vector<ClangTidyError> ClangTidyDiagnosticConsumer::take() {
661  finalizeLastError();
662 
663  std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
664  Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
665  Errors.end());
666  if (RemoveIncompatibleErrors)
667  removeIncompatibleErrors();
668  return std::move(Errors);
669 }
llvm::Optional< std::string > Checks
Checks filter.
SourceLocation Loc
&#39;#&#39; location in the include directive
ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const
Creates a new ClangTidyOptions instance combined from all fields of this instance overridden by the f...
Read-only set of strings represented as a list of positive and negative globs.
GlobList(StringRef Globs)
GlobList is a comma-separated list of globs (only &#39;*&#39; metacharacter is supported) with optional &#39;-&#39; p...
const ClangTidyGlobalOptions & getGlobalOptions() const
Returns global options.
static bool LineIsMarkedWithNOLINTinMacro(const SourceManager &SM, SourceLocation Loc, unsigned DiagID, const ClangTidyContext &Context)
bool isCheckEnabled(StringRef CheckName) const
Returns true if the check is enabled for the CurrentFile.
bool contains(StringRef S)
Returns true if the pattern matches S.
llvm::Optional< std::string > HeaderFilterRegex
Output warnings from headers matching this filter.
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Contains options for clang-tidy.
static bool ConsumeNegativeIndicator(StringRef &GlobList)
llvm::Optional< bool > SystemHeaders
Output warnings from system headers matching HeaderFilterRegex.
bool operator<(const SymbolLocation::Position &L, const SymbolLocation::Position &R)
Definition: Index.h:79
Context Ctx
std::pair< unsigned, unsigned > LineRange
LineRange is a pair<start, end> (inclusive).
void setCurrentFile(StringRef File)
Should be called when starting to process new translation unit.
static cl::opt< bool > AllowEnablingAnalyzerAlphaCheckers("allow-enabling-analyzer-alpha-checkers", cl::init(false), cl::Hidden, cl::cat(ClangTidyCategory))
This option allows enabling the experimental alpha checkers from the static analyzer.
static llvm::Regex ConsumeGlob(StringRef &GlobList)
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc, StringRef Message, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors detected using this method.
llvm::Optional< ClangTidyProfiling::StorageParams > getProfileStorageParams() const
const LangOptions & getLangOpts() const
Gets the language options from the AST context.
ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory, bool IsWarningAsError)
StringRef getCheckName(unsigned DiagnosticID) const
Returns the name of the clang-tidy check which produced this diagnostic ID.
ClangTidyOptions getOptionsForFile(StringRef File) const
Returns options for File.
const ClangTidyOptions & getOptions() const
Returns options for CurrentFile.
void setASTContext(ASTContext *Context)
Sets ASTContext for the current translation unit.
std::vector< FileFilter > LineFilter
Output warnings from certain line ranges of certain files only.
const Decl * D
Definition: XRefs.cpp:79
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
void setProfileStoragePrefix(StringRef ProfilePrefix)
Control storage of profile date.
PathRef FileName
llvm::Optional< std::string > WarningsAsErrors
WarningsAsErrors filter.
void setSourceManager(SourceManager *SourceMgr)
Sets the SourceManager of the used DiagnosticsEngine.
FunctionInfo Info
Contains a list of line ranges in a single file.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
ClangTidyContext(std::unique_ptr< ClangTidyOptionsProvider > OptionsProvider, bool AllowEnablingAnalyzerAlphaCheckers=false)
Initializes ClangTidyContext instance.
static bool IsNOLINTFound(StringRef NolintDirectiveText, StringRef Line, unsigned DiagID, const ClangTidyContext &Context)
std::vector< FixItHint > Hints
CharSourceRange Range
SourceRange for the file name.
A detected error complete with information to display diagnostic and automatic fix.
static cl::opt< std::string > Checks("checks", cl::desc(R"( Comma-separated list of globs with optional '-' prefix. Globs are processed in order of appearance in the list. Globs without '-' prefix add checks with matching names to the set, globs with the '-' prefix remove checks with matching names from the set of enabled checks. This option's value is appended to the value of the 'Checks' option in .clang-tidy file, if any. )"), cl::init(""), cl::cat(ClangTidyCategory))
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
llvm::Optional< FixItHint > FixIt
static ClangTidyOptions getDefaults()
These options are used for all settings that haven&#39;t been overridden by the OptionsProvider.
const char * Description
Definition: Dexp.cpp:258
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))
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override
bool treatAsError(StringRef CheckName) const
Returns true if the check should be upgraded to error for the CurrentFile.
void setEnableProfiling(bool Profile)
Control profile collection in clang-tidy.
static bool LineIsMarkedWithNOLINT(const SourceManager &SM, SourceLocation Loc, unsigned DiagID, const ClangTidyContext &Context)
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx, bool RemoveIncompatibleErrors=true)
const std::string & getCurrentBuildDirectory()
Returns build directory of the current translation unit.