clang-tools  4.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(SourceLocation Loc, PresumedLoc PLoc,
40  DiagnosticsEngine::Level Level, StringRef Message,
41  ArrayRef<CharSourceRange> Ranges,
42  const SourceManager *SM,
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 = Loc.isValid()
53  ? tooling::DiagnosticMessage(Message, *SM, 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(SourceLocation Loc, PresumedLoc PLoc,
64  DiagnosticsEngine::Level Level,
65  ArrayRef<CharSourceRange> Ranges,
66  const SourceManager &SM) override {}
67 
68  void emitCodeContext(SourceLocation Loc, DiagnosticsEngine::Level Level,
69  SmallVectorImpl<CharSourceRange> &Ranges,
70  ArrayRef<FixItHint> Hints,
71  const SourceManager &SM) override {
72  assert(Loc.isValid());
73  for (const auto &FixIt : Hints) {
74  CharSourceRange Range = FixIt.RemoveRange;
75  assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
76  "Invalid range in the fix-it hint.");
77  assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
78  "Only file locations supported in fix-it hints.");
79 
80  tooling::Replacement Replacement(SM, Range, 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(SourceLocation Loc, PresumedLoc PLoc,
93  const SourceManager &SM) override {}
94 
95  void emitImportLocation(SourceLocation Loc, PresumedLoc PLoc,
96  StringRef ModuleName,
97  const SourceManager &SM) override {}
98 
99  void emitBuildingModuleLocation(SourceLocation Loc, PresumedLoc PLoc,
100  StringRef ModuleName,
101  const SourceManager &SM) override {}
102 
103  void endDiagnostic(DiagOrStoredDiag D,
104  DiagnosticsEngine::Level Level) override {
105  assert(!Error.Message.Message.empty() && "Message has not been set");
106  }
107 
108 private:
109  ClangTidyError &Error;
110 };
111 } // end anonymous namespace
112 
113 ClangTidyError::ClangTidyError(StringRef CheckName,
114  ClangTidyError::Level DiagLevel,
115  StringRef BuildDirectory, bool IsWarningAsError)
116  : tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
117  IsWarningAsError(IsWarningAsError) {}
118 
119 // Returns true if GlobList starts with the negative indicator ('-'), removes it
120 // from the GlobList.
121 static bool ConsumeNegativeIndicator(StringRef &GlobList) {
122  if (GlobList.startswith("-")) {
123  GlobList = GlobList.substr(1);
124  return true;
125  }
126  return false;
127 }
128 // Converts first glob from the comma-separated list of globs to Regex and
129 // removes it and the trailing comma from the GlobList.
130 static llvm::Regex ConsumeGlob(StringRef &GlobList) {
131  StringRef Glob = GlobList.substr(0, GlobList.find(',')).trim();
132  GlobList = GlobList.substr(Glob.size() + 1);
133  SmallString<128> RegexText("^");
134  StringRef MetaChars("()^$|*+?.[]\\{}");
135  for (char C : Glob) {
136  if (C == '*')
137  RegexText.push_back('.');
138  else if (MetaChars.find(C) != StringRef::npos)
139  RegexText.push_back('\\');
140  RegexText.push_back(C);
141  }
142  RegexText.push_back('$');
143  return llvm::Regex(RegexText);
144 }
145 
146 GlobList::GlobList(StringRef Globs)
147  : Positive(!ConsumeNegativeIndicator(Globs)), Regex(ConsumeGlob(Globs)),
148  NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}
149 
150 bool GlobList::contains(StringRef S, bool Contains) {
151  if (Regex.match(S))
152  Contains = Positive;
153 
154  if (NextGlob)
155  Contains = NextGlob->contains(S, Contains);
156  return Contains;
157 }
158 
160  std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
161  : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
162  Profile(nullptr) {
163  // Before the first translation unit we can get errors related to command-line
164  // parsing, use empty string for the file name in this case.
165  setCurrentFile("");
166 }
167 
168 DiagnosticBuilder ClangTidyContext::diag(
169  StringRef CheckName, SourceLocation Loc, StringRef Description,
170  DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
171  assert(Loc.isValid());
172  unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
173  Level, (Description + " [" + CheckName + "]").str());
174  CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
175  return DiagEngine->Report(Loc, ID);
176 }
177 
178 void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {
179  DiagEngine = Engine;
180 }
181 
183  DiagEngine->setSourceManager(SourceMgr);
184 }
185 
187  CurrentFile = File;
188  CurrentOptions = getOptionsForFile(CurrentFile);
189  CheckFilter.reset(new GlobList(*getOptions().Checks));
190  WarningAsErrorFilter.reset(new GlobList(*getOptions().WarningsAsErrors));
191 }
192 
194  DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
195  LangOpts = Context->getLangOpts();
196 }
197 
199  return OptionsProvider->getGlobalOptions();
200 }
201 
203  return CurrentOptions;
204 }
205 
207  // Merge options on top of getDefaults() as a safeguard against options with
208  // unset values.
210  OptionsProvider->getOptions(File));
211 }
212 
214 
216  assert(CheckFilter != nullptr);
217  return *CheckFilter;
218 }
219 
221  assert(WarningAsErrorFilter != nullptr);
222  return *WarningAsErrorFilter;
223 }
224 
225 /// \brief Store a \c ClangTidyError.
226 void ClangTidyContext::storeError(const ClangTidyError &Error) {
227  Errors.push_back(Error);
228 }
229 
230 StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
231  llvm::DenseMap<unsigned, std::string>::const_iterator I =
232  CheckNamesByDiagnosticID.find(DiagnosticID);
233  if (I != CheckNamesByDiagnosticID.end())
234  return I->second;
235  return "";
236 }
237 
239  : Context(Ctx), LastErrorRelatesToUserCode(false),
240  LastErrorPassesLineFilter(false), LastErrorWasIgnored(false) {
241  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
242  Diags.reset(new DiagnosticsEngine(
243  IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
244  /*ShouldOwnClient=*/false));
245  Context.setDiagnosticsEngine(Diags.get());
246 }
247 
248 void ClangTidyDiagnosticConsumer::finalizeLastError() {
249  if (!Errors.empty()) {
250  ClangTidyError &Error = Errors.back();
251  if (!Context.getChecksFilter().contains(Error.DiagnosticName) &&
252  Error.DiagLevel != ClangTidyError::Error) {
253  ++Context.Stats.ErrorsIgnoredCheckFilter;
254  Errors.pop_back();
255  } else if (!LastErrorRelatesToUserCode) {
256  ++Context.Stats.ErrorsIgnoredNonUserCode;
257  Errors.pop_back();
258  } else if (!LastErrorPassesLineFilter) {
259  ++Context.Stats.ErrorsIgnoredLineFilter;
260  Errors.pop_back();
261  } else {
262  ++Context.Stats.ErrorsDisplayed;
263  }
264  }
265  LastErrorRelatesToUserCode = false;
266  LastErrorPassesLineFilter = false;
267 }
268 
269 static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
270  bool Invalid;
271  const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
272  if (!Invalid) {
273  const char *P = CharacterData;
274  while (*P != '\0' && *P != '\r' && *P != '\n')
275  ++P;
276  StringRef RestOfLine(CharacterData, P - CharacterData + 1);
277  // FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
278  if (RestOfLine.find("NOLINT") != StringRef::npos) {
279  return true;
280  }
281  }
282  return false;
283 }
284 
285 static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM,
286  SourceLocation Loc) {
287  while (true) {
288  if (LineIsMarkedWithNOLINT(SM, Loc))
289  return true;
290  if (!Loc.isMacroID())
291  return false;
292  Loc = SM.getImmediateExpansionRange(Loc).first;
293  }
294  return false;
295 }
296 
298  DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
299  if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
300  return;
301 
302  if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&
303  DiagLevel != DiagnosticsEngine::Fatal &&
304  LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(),
305  Info.getLocation())) {
306  ++Context.Stats.ErrorsIgnoredNOLINT;
307  // Ignored a warning, should ignore related notes as well
308  LastErrorWasIgnored = true;
309  return;
310  }
311 
312  LastErrorWasIgnored = false;
313  // Count warnings/errors.
314  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
315 
316  if (DiagLevel == DiagnosticsEngine::Note) {
317  assert(!Errors.empty() &&
318  "A diagnostic note can only be appended to a message.");
319  } else {
320  finalizeLastError();
321  StringRef WarningOption =
322  Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(
323  Info.getID());
324  std::string CheckName = !WarningOption.empty()
325  ? ("clang-diagnostic-" + WarningOption).str()
326  : Context.getCheckName(Info.getID()).str();
327 
328  if (CheckName.empty()) {
329  // This is a compiler diagnostic without a warning option. Assign check
330  // name based on its level.
331  switch (DiagLevel) {
332  case DiagnosticsEngine::Error:
333  case DiagnosticsEngine::Fatal:
334  CheckName = "clang-diagnostic-error";
335  break;
336  case DiagnosticsEngine::Warning:
337  CheckName = "clang-diagnostic-warning";
338  break;
339  default:
340  CheckName = "clang-diagnostic-unknown";
341  break;
342  }
343  }
344 
345  ClangTidyError::Level Level = ClangTidyError::Warning;
346  if (DiagLevel == DiagnosticsEngine::Error ||
347  DiagLevel == DiagnosticsEngine::Fatal) {
348  // Force reporting of Clang errors regardless of filters and non-user
349  // code.
350  Level = ClangTidyError::Error;
351  LastErrorRelatesToUserCode = true;
352  LastErrorPassesLineFilter = true;
353  }
354  bool IsWarningAsError =
355  DiagLevel == DiagnosticsEngine::Warning &&
356  Context.getWarningAsErrorFilter().contains(CheckName);
357  Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
358  IsWarningAsError);
359  }
360 
361  ClangTidyDiagnosticRenderer Converter(
362  Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
363  Errors.back());
364  SmallString<100> Message;
365  Info.FormatDiagnostic(Message);
366  SourceManager *Sources = nullptr;
367  if (Info.hasSourceManager())
368  Sources = &Info.getSourceManager();
369  Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message,
370  Info.getRanges(), Info.getFixItHints(), Sources);
371 
372  checkFilters(Info.getLocation());
373 }
374 
375 bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
376  unsigned LineNumber) const {
377  if (Context.getGlobalOptions().LineFilter.empty())
378  return true;
379  for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
380  if (FileName.endswith(Filter.Name)) {
381  if (Filter.LineRanges.empty())
382  return true;
383  for (const FileFilter::LineRange &Range : Filter.LineRanges) {
384  if (Range.first <= LineNumber && LineNumber <= Range.second)
385  return true;
386  }
387  return false;
388  }
389  }
390  return false;
391 }
392 
393 void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {
394  // Invalid location may mean a diagnostic in a command line, don't skip these.
395  if (!Location.isValid()) {
396  LastErrorRelatesToUserCode = true;
397  LastErrorPassesLineFilter = true;
398  return;
399  }
400 
401  const SourceManager &Sources = Diags->getSourceManager();
402  if (!*Context.getOptions().SystemHeaders &&
403  Sources.isInSystemHeader(Location))
404  return;
405 
406  // FIXME: We start with a conservative approach here, but the actual type of
407  // location needed depends on the check (in particular, where this check wants
408  // to apply fixes).
409  FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
410  const FileEntry *File = Sources.getFileEntryForID(FID);
411 
412  // -DMACRO definitions on the command line have locations in a virtual buffer
413  // that doesn't have a FileEntry. Don't skip these as well.
414  if (!File) {
415  LastErrorRelatesToUserCode = true;
416  LastErrorPassesLineFilter = true;
417  return;
418  }
419 
420  StringRef FileName(File->getName());
421  LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
422  Sources.isInMainFile(Location) ||
423  getHeaderFilter()->match(FileName);
424 
425  unsigned LineNumber = Sources.getExpansionLineNumber(Location);
426  LastErrorPassesLineFilter =
427  LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
428 }
429 
430 llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
431  if (!HeaderFilter)
432  HeaderFilter.reset(
433  new llvm::Regex(*Context.getOptions().HeaderFilterRegex));
434  return HeaderFilter.get();
435 }
436 
437 void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
438  SmallVectorImpl<ClangTidyError> &Errors) const {
439  // Each error is modelled as the set of intervals in which it applies
440  // replacements. To detect overlapping replacements, we use a sweep line
441  // algorithm over these sets of intervals.
442  // An event here consists of the opening or closing of an interval. During the
443  // process, we maintain a counter with the amount of open intervals. If we
444  // find an endpoint of an interval and this counter is different from 0, it
445  // means that this interval overlaps with another one, so we set it as
446  // inapplicable.
447  struct Event {
448  // An event can be either the begin or the end of an interval.
449  enum EventType {
450  ET_Begin = 1,
451  ET_End = -1,
452  };
453 
454  Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,
455  unsigned ErrorSize)
456  : Type(Type), ErrorId(ErrorId) {
457  // The events are going to be sorted by their position. In case of draw:
458  //
459  // * If an interval ends at the same position at which other interval
460  // begins, this is not an overlapping, so we want to remove the ending
461  // interval before adding the starting one: end events have higher
462  // priority than begin events.
463  //
464  // * If we have several begin points at the same position, we will mark as
465  // inapplicable the ones that we process later, so the first one has to
466  // be the one with the latest end point, because this one will contain
467  // all the other intervals. For the same reason, if we have several end
468  // points in the same position, the last one has to be the one with the
469  // earliest begin point. In both cases, we sort non-increasingly by the
470  // position of the complementary.
471  //
472  // * In case of two equal intervals, the one whose error is bigger can
473  // potentially contain the other one, so we want to process its begin
474  // points before and its end points later.
475  //
476  // * Finally, if we have two equal intervals whose errors have the same
477  // size, none of them will be strictly contained inside the other.
478  // Sorting by ErrorId will guarantee that the begin point of the first
479  // one will be processed before, disallowing the second one, and the
480  // end point of the first one will also be processed before,
481  // disallowing the first one.
482  if (Type == ET_Begin)
483  Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
484  else
485  Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
486  }
487 
488  bool operator<(const Event &Other) const {
489  return Priority < Other.Priority;
490  }
491 
492  // Determines if this event is the begin or the end of an interval.
493  EventType Type;
494  // The index of the error to which the interval that generated this event
495  // belongs.
496  unsigned ErrorId;
497  // The events will be sorted based on this field.
498  std::tuple<unsigned, EventType, int, int, unsigned> Priority;
499  };
500 
501  // Compute error sizes.
502  std::vector<int> Sizes;
503  for (const auto &Error : Errors) {
504  int Size = 0;
505  for (const auto &FileAndReplaces : Error.Fix) {
506  for (const auto &Replace : FileAndReplaces.second)
507  Size += Replace.getLength();
508  }
509  Sizes.push_back(Size);
510  }
511 
512  // Build events from error intervals.
513  std::map<std::string, std::vector<Event>> FileEvents;
514  for (unsigned I = 0; I < Errors.size(); ++I) {
515  for (const auto &FileAndReplace : Errors[I].Fix) {
516  for (const auto &Replace : FileAndReplace.second) {
517  unsigned Begin = Replace.getOffset();
518  unsigned End = Begin + Replace.getLength();
519  const std::string &FilePath = Replace.getFilePath();
520  // FIXME: Handle empty intervals, such as those from insertions.
521  if (Begin == End)
522  continue;
523  auto &Events = FileEvents[FilePath];
524  Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
525  Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
526  }
527  }
528  }
529 
530  std::vector<bool> Apply(Errors.size(), true);
531  for (auto &FileAndEvents : FileEvents) {
532  std::vector<Event> &Events = FileAndEvents.second;
533  // Sweep.
534  std::sort(Events.begin(), Events.end());
535  int OpenIntervals = 0;
536  for (const auto &Event : Events) {
537  if (Event.Type == Event::ET_End)
538  --OpenIntervals;
539  // This has to be checked after removing the interval from the count if it
540  // is an end event, or before adding it if it is a begin event.
541  if (OpenIntervals != 0)
542  Apply[Event.ErrorId] = false;
543  if (Event.Type == Event::ET_Begin)
544  ++OpenIntervals;
545  }
546  assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
547  }
548 
549  for (unsigned I = 0; I < Errors.size(); ++I) {
550  if (!Apply[I]) {
551  Errors[I].Fix.clear();
552  Errors[I].Notes.emplace_back(
553  "this fix will not be applied because it overlaps with another fix");
554  }
555  }
556 }
557 
558 namespace {
559 struct LessClangTidyError {
560  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
561  const tooling::DiagnosticMessage &M1 = LHS.Message;
562  const tooling::DiagnosticMessage &M2 = RHS.Message;
563 
564  return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
565  std::tie(M2.FilePath, M2.FileOffset, M2.Message);
566  }
567 };
568 struct EqualClangTidyError {
569  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
570  LessClangTidyError Less;
571  return !Less(LHS, RHS) && !Less(RHS, LHS);
572  }
573 };
574 } // end anonymous namespace
575 
576 // Flushes the internal diagnostics buffer to the ClangTidyContext.
578  finalizeLastError();
579 
580  std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
581  Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
582  Errors.end());
583  removeIncompatibleErrors(Errors);
584 
585  for (const ClangTidyError &Error : Errors)
586  Context.storeError(Error);
587  Errors.clear();
588 }
SourceLocation Loc
'#' location in the include directive
LangOptions LangOpts
Definition: ClangTidy.cpp:240
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...
std::vector< std::unique_ptr< ClangTidyCheck > > Checks
Definition: ClangTidy.cpp:263
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:244
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...
GlobList & getWarningAsErrorFilter()
Returns check filter for the CurrentFile which selects checks for upgrade to error.
static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM, SourceLocation Loc)
unsigned WarningsAsErrors
Definition: ClangTidy.cpp:249
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:241
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc)
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
GlobList & getChecksFilter()
Returns check filter for the CurrentFile.
Container for clang-tidy profiling data.
const std::string & getCurrentBuildDirectory()
Returns build directory of the current translation unit.