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