[clang-tidy] Fix treating non-space whitespaces in checks list.

Summary:
This furtherly improves r295303: [clang-tidy] Ignore spaces between globs in the Checks option.
Trims all whitespaces and not only spaces and correctly computes the offset of the checks list (taking the size before trimming).

Reviewers: alexfh

Reviewed By: alexfh

Subscribers: cfe-commits, JDevlieghere

Differential Revision: https://reviews.llvm.org/D30567

llvm-svn: 298621
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
index 3438a61..c4a5ede 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -1,618 +1,619 @@
-//===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-///  \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
-///  and ClangTidyError classes.
-///
-///  This tool uses the Clang Tooling infrastructure, see
-///    http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
-///  for details on setting it up with LLVM source tree.
-///
-//===----------------------------------------------------------------------===//
-
-#include "ClangTidyDiagnosticConsumer.h"
-#include "ClangTidyOptions.h"
-#include "clang/AST/ASTDiagnostic.h"
-#include "clang/Basic/DiagnosticOptions.h"
-#include "clang/Frontend/DiagnosticRenderer.h"
-#include "llvm/ADT/SmallString.h"
-#include <tuple>
-#include <vector>
-using namespace clang;
-using namespace tidy;
-
-namespace {
-class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {
-public:
-  ClangTidyDiagnosticRenderer(const LangOptions &LangOpts,
-                              DiagnosticOptions *DiagOpts,
-                              ClangTidyError &Error)
-      : DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
-
-protected:
-  void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc,
-                             DiagnosticsEngine::Level Level, StringRef Message,
-                             ArrayRef<CharSourceRange> Ranges,
-                             const SourceManager *SM,
-                             DiagOrStoredDiag Info) override {
-    // Remove check name from the message.
-    // FIXME: Remove this once there's a better way to pass check names than
-    // appending the check name to the message in ClangTidyContext::diag and
-    // using getCustomDiagID.
-    std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";
-    if (Message.endswith(CheckNameInMessage))
-      Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
-
-    auto TidyMessage = Loc.isValid()
-                           ? tooling::DiagnosticMessage(Message, *SM, Loc)
-                           : tooling::DiagnosticMessage(Message);
-    if (Level == DiagnosticsEngine::Note) {
-      Error.Notes.push_back(TidyMessage);
-      return;
-    }
-    assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
-    Error.Message = TidyMessage;
-  }
-
-  void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
-                         DiagnosticsEngine::Level Level,
-                         ArrayRef<CharSourceRange> Ranges,
-                         const SourceManager &SM) override {}
-
-  void emitCodeContext(SourceLocation Loc, DiagnosticsEngine::Level Level,
-                       SmallVectorImpl<CharSourceRange> &Ranges,
-                       ArrayRef<FixItHint> Hints,
-                       const SourceManager &SM) override {
-    assert(Loc.isValid());
-    for (const auto &FixIt : Hints) {
-      CharSourceRange Range = FixIt.RemoveRange;
-      assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
-             "Invalid range in the fix-it hint.");
-      assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
-             "Only file locations supported in fix-it hints.");
-
-      tooling::Replacement Replacement(SM, Range, FixIt.CodeToInsert);
-      llvm::Error Err = Error.Fix[Replacement.getFilePath()].add(Replacement);
-      // FIXME: better error handling (at least, don't let other replacements be
-      // applied).
-      if (Err) {
-        llvm::errs() << "Fix conflicts with existing fix! "
-                     << llvm::toString(std::move(Err)) << "\n";
-        assert(false && "Fix conflicts with existing fix!");
-      }
-    }
-  }
-
-  void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc,
-                           const SourceManager &SM) override {}
-
-  void emitImportLocation(SourceLocation Loc, PresumedLoc PLoc,
-                          StringRef ModuleName,
-                          const SourceManager &SM) override {}
-
-  void emitBuildingModuleLocation(SourceLocation Loc, PresumedLoc PLoc,
-                                  StringRef ModuleName,
-                                  const SourceManager &SM) override {}
-
-  void endDiagnostic(DiagOrStoredDiag D,
-                     DiagnosticsEngine::Level Level) override {
-    assert(!Error.Message.Message.empty() && "Message has not been set");
-  }
-
-private:
-  ClangTidyError &Error;
-};
-} // end anonymous namespace
-
-ClangTidyError::ClangTidyError(StringRef CheckName,
-                               ClangTidyError::Level DiagLevel,
-                               StringRef BuildDirectory, bool IsWarningAsError)
-    : tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
-      IsWarningAsError(IsWarningAsError) {}
-
-// Returns true if GlobList starts with the negative indicator ('-'), removes it
-// from the GlobList.
-static bool ConsumeNegativeIndicator(StringRef &GlobList) {
-  GlobList = GlobList.trim(' ');
-  if (GlobList.startswith("-")) {
-    GlobList = GlobList.substr(1);
-    return true;
-  }
-  return false;
-}
-// Converts first glob from the comma-separated list of globs to Regex and
-// removes it and the trailing comma from the GlobList.
-static llvm::Regex ConsumeGlob(StringRef &GlobList) {
-  StringRef Glob = GlobList.substr(0, GlobList.find(',')).trim();
-  GlobList = GlobList.substr(Glob.size() + 1);
-  SmallString<128> RegexText("^");
-  StringRef MetaChars("()^$|*+?.[]\\{}");
-  for (char C : Glob) {
-    if (C == '*')
-      RegexText.push_back('.');
-    else if (MetaChars.find(C) != StringRef::npos)
-      RegexText.push_back('\\');
-    RegexText.push_back(C);
-  }
-  RegexText.push_back('$');
-  return llvm::Regex(RegexText);
-}
-
-GlobList::GlobList(StringRef Globs)
-    : Positive(!ConsumeNegativeIndicator(Globs)), Regex(ConsumeGlob(Globs)),
-      NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}
-
-bool GlobList::contains(StringRef S, bool Contains) {
-  if (Regex.match(S))
-    Contains = Positive;
-
-  if (NextGlob)
-    Contains = NextGlob->contains(S, Contains);
-  return Contains;
-}
-
-ClangTidyContext::ClangTidyContext(
-    std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
-    : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
-      Profile(nullptr) {
-  // Before the first translation unit we can get errors related to command-line
-  // parsing, use empty string for the file name in this case.
-  setCurrentFile("");
-}
-
-DiagnosticBuilder ClangTidyContext::diag(
-    StringRef CheckName, SourceLocation Loc, StringRef Description,
-    DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
-  assert(Loc.isValid());
-  unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
-      Level, (Description + " [" + CheckName + "]").str());
-  CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
-  return DiagEngine->Report(Loc, ID);
-}
-
-void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {
-  DiagEngine = Engine;
-}
-
-void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
-  DiagEngine->setSourceManager(SourceMgr);
-}
-
-void ClangTidyContext::setCurrentFile(StringRef File) {
-  CurrentFile = File;
-  CurrentOptions = getOptionsForFile(CurrentFile);
-  CheckFilter.reset(new GlobList(*getOptions().Checks));
-  WarningAsErrorFilter.reset(new GlobList(*getOptions().WarningsAsErrors));
-}
-
-void ClangTidyContext::setASTContext(ASTContext *Context) {
-  DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
-  LangOpts = Context->getLangOpts();
-}
-
-const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {
-  return OptionsProvider->getGlobalOptions();
-}
-
-const ClangTidyOptions &ClangTidyContext::getOptions() const {
-  return CurrentOptions;
-}
-
-ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {
-  // Merge options on top of getDefaults() as a safeguard against options with
-  // unset values.
-  return ClangTidyOptions::getDefaults().mergeWith(
-      OptionsProvider->getOptions(File));
-}
-
-void ClangTidyContext::setCheckProfileData(ProfileData *P) { Profile = P; }
-
-GlobList &ClangTidyContext::getChecksFilter() {
-  assert(CheckFilter != nullptr);
-  return *CheckFilter;
-}
-
-GlobList &ClangTidyContext::getWarningAsErrorFilter() {
-  assert(WarningAsErrorFilter != nullptr);
-  return *WarningAsErrorFilter;
-}
-
-/// \brief Store a \c ClangTidyError.
-void ClangTidyContext::storeError(const ClangTidyError &Error) {
-  Errors.push_back(Error);
-}
-
-StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
-  llvm::DenseMap<unsigned, std::string>::const_iterator I =
-      CheckNamesByDiagnosticID.find(DiagnosticID);
-  if (I != CheckNamesByDiagnosticID.end())
-    return I->second;
-  return "";
-}
-
-ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
-    : Context(Ctx), LastErrorRelatesToUserCode(false),
-      LastErrorPassesLineFilter(false), LastErrorWasIgnored(false) {
-  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
-  Diags.reset(new DiagnosticsEngine(
-      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
-      /*ShouldOwnClient=*/false));
-  Context.setDiagnosticsEngine(Diags.get());
-}
-
-void ClangTidyDiagnosticConsumer::finalizeLastError() {
-  if (!Errors.empty()) {
-    ClangTidyError &Error = Errors.back();
-    if (!Context.getChecksFilter().contains(Error.DiagnosticName) &&
-        Error.DiagLevel != ClangTidyError::Error) {
-      ++Context.Stats.ErrorsIgnoredCheckFilter;
-      Errors.pop_back();
-    } else if (!LastErrorRelatesToUserCode) {
-      ++Context.Stats.ErrorsIgnoredNonUserCode;
-      Errors.pop_back();
-    } else if (!LastErrorPassesLineFilter) {
-      ++Context.Stats.ErrorsIgnoredLineFilter;
-      Errors.pop_back();
-    } else {
-      ++Context.Stats.ErrorsDisplayed;
-    }
-  }
-  LastErrorRelatesToUserCode = false;
-  LastErrorPassesLineFilter = false;
-}
-
-static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
-  bool Invalid;
-  const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
-  if (Invalid)
-    return false;
-
-  // Check if there's a NOLINT on this line.
-  const char *P = CharacterData;
-  while (*P != '\0' && *P != '\r' && *P != '\n')
-    ++P;
-  StringRef RestOfLine(CharacterData, P - CharacterData + 1);
-  // FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
-  if (RestOfLine.find("NOLINT") != StringRef::npos)
-    return true;
-
-  // Check if there's a NOLINTNEXTLINE on the previous line.
-  const char *BufBegin =
-      SM.getCharacterData(SM.getLocForStartOfFile(SM.getFileID(Loc)), &Invalid);
-  if (Invalid || P == BufBegin)
-    return false;
-
-  // Scan backwards over the current line.
-  P = CharacterData;
-  while (P != BufBegin && *P != '\n')
-    --P;
-
-  // If we reached the begin of the file there is no line before it.
-  if (P == BufBegin)
-    return false;
-
-  // Skip over the newline.
-  --P;
-  const char *LineEnd = P;
-
-  // Now we're on the previous line. Skip to the beginning of it.
-  while (P != BufBegin && *P != '\n')
-    --P;
-
-  RestOfLine = StringRef(P, LineEnd - P + 1);
-  if (RestOfLine.find("NOLINTNEXTLINE") != StringRef::npos)
-    return true;
-
-  return false;
-}
-
-static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM,
-                                          SourceLocation Loc) {
-  while (true) {
-    if (LineIsMarkedWithNOLINT(SM, Loc))
-      return true;
-    if (!Loc.isMacroID())
-      return false;
-    Loc = SM.getImmediateExpansionRange(Loc).first;
-  }
-  return false;
-}
-
-void ClangTidyDiagnosticConsumer::HandleDiagnostic(
-    DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
-  if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
-    return;
-
-  if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&
-      DiagLevel != DiagnosticsEngine::Fatal &&
-      LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(),
-                                    Info.getLocation())) {
-    ++Context.Stats.ErrorsIgnoredNOLINT;
-    // Ignored a warning, should ignore related notes as well
-    LastErrorWasIgnored = true;
-    return;
-  }
-
-  LastErrorWasIgnored = false;
-  // Count warnings/errors.
-  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
-
-  if (DiagLevel == DiagnosticsEngine::Note) {
-    assert(!Errors.empty() &&
-           "A diagnostic note can only be appended to a message.");
-  } else {
-    finalizeLastError();
-    StringRef WarningOption =
-        Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(
-            Info.getID());
-    std::string CheckName = !WarningOption.empty()
-                                ? ("clang-diagnostic-" + WarningOption).str()
-                                : Context.getCheckName(Info.getID()).str();
-
-    if (CheckName.empty()) {
-      // This is a compiler diagnostic without a warning option. Assign check
-      // name based on its level.
-      switch (DiagLevel) {
-      case DiagnosticsEngine::Error:
-      case DiagnosticsEngine::Fatal:
-        CheckName = "clang-diagnostic-error";
-        break;
-      case DiagnosticsEngine::Warning:
-        CheckName = "clang-diagnostic-warning";
-        break;
-      default:
-        CheckName = "clang-diagnostic-unknown";
-        break;
-      }
-    }
-
-    ClangTidyError::Level Level = ClangTidyError::Warning;
-    if (DiagLevel == DiagnosticsEngine::Error ||
-        DiagLevel == DiagnosticsEngine::Fatal) {
-      // Force reporting of Clang errors regardless of filters and non-user
-      // code.
-      Level = ClangTidyError::Error;
-      LastErrorRelatesToUserCode = true;
-      LastErrorPassesLineFilter = true;
-    }
-    bool IsWarningAsError =
-        DiagLevel == DiagnosticsEngine::Warning &&
-        Context.getWarningAsErrorFilter().contains(CheckName);
-    Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
-                        IsWarningAsError);
-  }
-
-  ClangTidyDiagnosticRenderer Converter(
-      Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
-      Errors.back());
-  SmallString<100> Message;
-  Info.FormatDiagnostic(Message);
-  SourceManager *Sources = nullptr;
-  if (Info.hasSourceManager())
-    Sources = &Info.getSourceManager();
-  Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message,
-                           Info.getRanges(), Info.getFixItHints(), Sources);
-
-  checkFilters(Info.getLocation());
-}
-
-bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
-                                                   unsigned LineNumber) const {
-  if (Context.getGlobalOptions().LineFilter.empty())
-    return true;
-  for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
-    if (FileName.endswith(Filter.Name)) {
-      if (Filter.LineRanges.empty())
-        return true;
-      for (const FileFilter::LineRange &Range : Filter.LineRanges) {
-        if (Range.first <= LineNumber && LineNumber <= Range.second)
-          return true;
-      }
-      return false;
-    }
-  }
-  return false;
-}
-
-void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {
-  // Invalid location may mean a diagnostic in a command line, don't skip these.
-  if (!Location.isValid()) {
-    LastErrorRelatesToUserCode = true;
-    LastErrorPassesLineFilter = true;
-    return;
-  }
-
-  const SourceManager &Sources = Diags->getSourceManager();
-  if (!*Context.getOptions().SystemHeaders &&
-      Sources.isInSystemHeader(Location))
-    return;
-
-  // FIXME: We start with a conservative approach here, but the actual type of
-  // location needed depends on the check (in particular, where this check wants
-  // to apply fixes).
-  FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
-  const FileEntry *File = Sources.getFileEntryForID(FID);
-
-  // -DMACRO definitions on the command line have locations in a virtual buffer
-  // that doesn't have a FileEntry. Don't skip these as well.
-  if (!File) {
-    LastErrorRelatesToUserCode = true;
-    LastErrorPassesLineFilter = true;
-    return;
-  }
-
-  StringRef FileName(File->getName());
-  LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
-                               Sources.isInMainFile(Location) ||
-                               getHeaderFilter()->match(FileName);
-
-  unsigned LineNumber = Sources.getExpansionLineNumber(Location);
-  LastErrorPassesLineFilter =
-      LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
-}
-
-llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
-  if (!HeaderFilter)
-    HeaderFilter.reset(
-        new llvm::Regex(*Context.getOptions().HeaderFilterRegex));
-  return HeaderFilter.get();
-}
-
-void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
-    SmallVectorImpl<ClangTidyError> &Errors) const {
-  // Each error is modelled as the set of intervals in which it applies
-  // replacements. To detect overlapping replacements, we use a sweep line
-  // algorithm over these sets of intervals.
-  // An event here consists of the opening or closing of an interval. During the
-  // process, we maintain a counter with the amount of open intervals. If we
-  // find an endpoint of an interval and this counter is different from 0, it
-  // means that this interval overlaps with another one, so we set it as
-  // inapplicable.
-  struct Event {
-    // An event can be either the begin or the end of an interval.
-    enum EventType {
-      ET_Begin = 1,
-      ET_End = -1,
-    };
-
-    Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,
-          unsigned ErrorSize)
-        : Type(Type), ErrorId(ErrorId) {
-      // The events are going to be sorted by their position. In case of draw:
-      //
-      // * If an interval ends at the same position at which other interval
-      //   begins, this is not an overlapping, so we want to remove the ending
-      //   interval before adding the starting one: end events have higher
-      //   priority than begin events.
-      //
-      // * If we have several begin points at the same position, we will mark as
-      //   inapplicable the ones that we process later, so the first one has to
-      //   be the one with the latest end point, because this one will contain
-      //   all the other intervals. For the same reason, if we have several end
-      //   points in the same position, the last one has to be the one with the
-      //   earliest begin point. In both cases, we sort non-increasingly by the
-      //   position of the complementary.
-      //
-      // * In case of two equal intervals, the one whose error is bigger can
-      //   potentially contain the other one, so we want to process its begin
-      //   points before and its end points later.
-      //
-      // * Finally, if we have two equal intervals whose errors have the same
-      //   size, none of them will be strictly contained inside the other.
-      //   Sorting by ErrorId will guarantee that the begin point of the first
-      //   one will be processed before, disallowing the second one, and the
-      //   end point of the first one will also be processed before,
-      //   disallowing the first one.
-      if (Type == ET_Begin)
-        Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
-      else
-        Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
-    }
-
-    bool operator<(const Event &Other) const {
-      return Priority < Other.Priority;
-    }
-
-    // Determines if this event is the begin or the end of an interval.
-    EventType Type;
-    // The index of the error to which the interval that generated this event
-    // belongs.
-    unsigned ErrorId;
-    // The events will be sorted based on this field.
-    std::tuple<unsigned, EventType, int, int, unsigned> Priority;
-  };
-
-  // Compute error sizes.
-  std::vector<int> Sizes;
-  for (const auto &Error : Errors) {
-    int Size = 0;
-    for (const auto &FileAndReplaces : Error.Fix) {
-      for (const auto &Replace : FileAndReplaces.second)
-        Size += Replace.getLength();
-    }
-    Sizes.push_back(Size);
-  }
-
-  // Build events from error intervals.
-  std::map<std::string, std::vector<Event>> FileEvents;
-  for (unsigned I = 0; I < Errors.size(); ++I) {
-    for (const auto &FileAndReplace : Errors[I].Fix) {
-      for (const auto &Replace : FileAndReplace.second) {
-        unsigned Begin = Replace.getOffset();
-        unsigned End = Begin + Replace.getLength();
-        const std::string &FilePath = Replace.getFilePath();
-        // FIXME: Handle empty intervals, such as those from insertions.
-        if (Begin == End)
-          continue;
-        auto &Events = FileEvents[FilePath];
-        Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
-        Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
-      }
-    }
-  }
-
-  std::vector<bool> Apply(Errors.size(), true);
-  for (auto &FileAndEvents : FileEvents) {
-    std::vector<Event> &Events = FileAndEvents.second;
-    // Sweep.
-    std::sort(Events.begin(), Events.end());
-    int OpenIntervals = 0;
-    for (const auto &Event : Events) {
-      if (Event.Type == Event::ET_End)
-        --OpenIntervals;
-      // This has to be checked after removing the interval from the count if it
-      // is an end event, or before adding it if it is a begin event.
-      if (OpenIntervals != 0)
-        Apply[Event.ErrorId] = false;
-      if (Event.Type == Event::ET_Begin)
-        ++OpenIntervals;
-    }
-    assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
-  }
-
-  for (unsigned I = 0; I < Errors.size(); ++I) {
-    if (!Apply[I]) {
-      Errors[I].Fix.clear();
-      Errors[I].Notes.emplace_back(
-          "this fix will not be applied because it overlaps with another fix");
-    }
-  }
-}
-
-namespace {
-struct LessClangTidyError {
-  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
-    const tooling::DiagnosticMessage &M1 = LHS.Message;
-    const tooling::DiagnosticMessage &M2 = RHS.Message;
-
-    return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
-           std::tie(M2.FilePath, M2.FileOffset, M2.Message);
-  }
-};
-struct EqualClangTidyError {
-  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
-    LessClangTidyError Less;
-    return !Less(LHS, RHS) && !Less(RHS, LHS);
-  }
-};
-} // end anonymous namespace
-
-// Flushes the internal diagnostics buffer to the ClangTidyContext.
-void ClangTidyDiagnosticConsumer::finish() {
-  finalizeLastError();
-
-  std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
-  Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
-               Errors.end());
-  removeIncompatibleErrors(Errors);
-
-  for (const ClangTidyError &Error : Errors)
-    Context.storeError(Error);
-  Errors.clear();
-}
+//===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //

+//

+//                     The LLVM Compiler Infrastructure

+//

+// This file is distributed under the University of Illinois Open Source

+// License. See LICENSE.TXT for details.

+//

+//===----------------------------------------------------------------------===//

+///

+///  \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext

+///  and ClangTidyError classes.

+///

+///  This tool uses the Clang Tooling infrastructure, see

+///    http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html

+///  for details on setting it up with LLVM source tree.

+///

+//===----------------------------------------------------------------------===//

+

+#include "ClangTidyDiagnosticConsumer.h"

+#include "ClangTidyOptions.h"

+#include "clang/AST/ASTDiagnostic.h"

+#include "clang/Basic/DiagnosticOptions.h"

+#include "clang/Frontend/DiagnosticRenderer.h"

+#include "llvm/ADT/SmallString.h"

+#include <tuple>

+#include <vector>

+using namespace clang;

+using namespace tidy;

+

+namespace {

+class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {

+public:

+  ClangTidyDiagnosticRenderer(const LangOptions &LangOpts,

+                              DiagnosticOptions *DiagOpts,

+                              ClangTidyError &Error)

+      : DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}

+

+protected:

+  void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc,

+                             DiagnosticsEngine::Level Level, StringRef Message,

+                             ArrayRef<CharSourceRange> Ranges,

+                             const SourceManager *SM,

+                             DiagOrStoredDiag Info) override {

+    // Remove check name from the message.

+    // FIXME: Remove this once there's a better way to pass check names than

+    // appending the check name to the message in ClangTidyContext::diag and

+    // using getCustomDiagID.

+    std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";

+    if (Message.endswith(CheckNameInMessage))

+      Message = Message.substr(0, Message.size() - CheckNameInMessage.size());

+

+    auto TidyMessage = Loc.isValid()

+                           ? tooling::DiagnosticMessage(Message, *SM, Loc)

+                           : tooling::DiagnosticMessage(Message);

+    if (Level == DiagnosticsEngine::Note) {

+      Error.Notes.push_back(TidyMessage);

+      return;

+    }

+    assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");

+    Error.Message = TidyMessage;

+  }

+

+  void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,

+                         DiagnosticsEngine::Level Level,

+                         ArrayRef<CharSourceRange> Ranges,

+                         const SourceManager &SM) override {}

+

+  void emitCodeContext(SourceLocation Loc, DiagnosticsEngine::Level Level,

+                       SmallVectorImpl<CharSourceRange> &Ranges,

+                       ArrayRef<FixItHint> Hints,

+                       const SourceManager &SM) override {

+    assert(Loc.isValid());

+    for (const auto &FixIt : Hints) {

+      CharSourceRange Range = FixIt.RemoveRange;

+      assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&

+             "Invalid range in the fix-it hint.");

+      assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&

+             "Only file locations supported in fix-it hints.");

+

+      tooling::Replacement Replacement(SM, Range, FixIt.CodeToInsert);

+      llvm::Error Err = Error.Fix[Replacement.getFilePath()].add(Replacement);

+      // FIXME: better error handling (at least, don't let other replacements be

+      // applied).

+      if (Err) {

+        llvm::errs() << "Fix conflicts with existing fix! "

+                     << llvm::toString(std::move(Err)) << "\n";

+        assert(false && "Fix conflicts with existing fix!");

+      }

+    }

+  }

+

+  void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc,

+                           const SourceManager &SM) override {}

+

+  void emitImportLocation(SourceLocation Loc, PresumedLoc PLoc,

+                          StringRef ModuleName,

+                          const SourceManager &SM) override {}

+

+  void emitBuildingModuleLocation(SourceLocation Loc, PresumedLoc PLoc,

+                                  StringRef ModuleName,

+                                  const SourceManager &SM) override {}

+

+  void endDiagnostic(DiagOrStoredDiag D,

+                     DiagnosticsEngine::Level Level) override {

+    assert(!Error.Message.Message.empty() && "Message has not been set");

+  }

+

+private:

+  ClangTidyError &Error;

+};

+} // end anonymous namespace

+

+ClangTidyError::ClangTidyError(StringRef CheckName,

+                               ClangTidyError::Level DiagLevel,

+                               StringRef BuildDirectory, bool IsWarningAsError)

+    : tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),

+      IsWarningAsError(IsWarningAsError) {}

+

+// Returns true if GlobList starts with the negative indicator ('-'), removes it

+// from the GlobList.

+static bool ConsumeNegativeIndicator(StringRef &GlobList) {

+  GlobList = GlobList.trim(' ');

+  if (GlobList.startswith("-")) {

+    GlobList = GlobList.substr(1);

+    return true;

+  }

+  return false;

+}

+// Converts first glob from the comma-separated list of globs to Regex and

+// removes it and the trailing comma from the GlobList.

+static llvm::Regex ConsumeGlob(StringRef &GlobList) {

+  StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find(','));

+  StringRef Glob = UntrimmedGlob.trim(' ');

+  GlobList = GlobList.substr(UntrimmedGlob.size() + 1);

+  SmallString<128> RegexText("^");

+  StringRef MetaChars("()^$|*+?.[]\\{}");

+  for (char C : Glob) {

+    if (C == '*')

+      RegexText.push_back('.');

+    else if (MetaChars.find(C) != StringRef::npos)

+      RegexText.push_back('\\');

+    RegexText.push_back(C);

+  }

+  RegexText.push_back('$');

+  return llvm::Regex(RegexText);

+}

+

+GlobList::GlobList(StringRef Globs)

+    : Positive(!ConsumeNegativeIndicator(Globs)), Regex(ConsumeGlob(Globs)),

+      NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}

+

+bool GlobList::contains(StringRef S, bool Contains) {

+  if (Regex.match(S))

+    Contains = Positive;

+

+  if (NextGlob)

+    Contains = NextGlob->contains(S, Contains);

+  return Contains;

+}

+

+ClangTidyContext::ClangTidyContext(

+    std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)

+    : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),

+      Profile(nullptr) {

+  // Before the first translation unit we can get errors related to command-line

+  // parsing, use empty string for the file name in this case.

+  setCurrentFile("");

+}

+

+DiagnosticBuilder ClangTidyContext::diag(

+    StringRef CheckName, SourceLocation Loc, StringRef Description,

+    DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {

+  assert(Loc.isValid());

+  unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(

+      Level, (Description + " [" + CheckName + "]").str());

+  CheckNamesByDiagnosticID.try_emplace(ID, CheckName);

+  return DiagEngine->Report(Loc, ID);

+}

+

+void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {

+  DiagEngine = Engine;

+}

+

+void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {

+  DiagEngine->setSourceManager(SourceMgr);

+}

+

+void ClangTidyContext::setCurrentFile(StringRef File) {

+  CurrentFile = File;

+  CurrentOptions = getOptionsForFile(CurrentFile);

+  CheckFilter.reset(new GlobList(*getOptions().Checks));

+  WarningAsErrorFilter.reset(new GlobList(*getOptions().WarningsAsErrors));

+}

+

+void ClangTidyContext::setASTContext(ASTContext *Context) {

+  DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);

+  LangOpts = Context->getLangOpts();

+}

+

+const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {

+  return OptionsProvider->getGlobalOptions();

+}

+

+const ClangTidyOptions &ClangTidyContext::getOptions() const {

+  return CurrentOptions;

+}

+

+ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {

+  // Merge options on top of getDefaults() as a safeguard against options with

+  // unset values.

+  return ClangTidyOptions::getDefaults().mergeWith(

+      OptionsProvider->getOptions(File));

+}

+

+void ClangTidyContext::setCheckProfileData(ProfileData *P) { Profile = P; }

+

+GlobList &ClangTidyContext::getChecksFilter() {

+  assert(CheckFilter != nullptr);

+  return *CheckFilter;

+}

+

+GlobList &ClangTidyContext::getWarningAsErrorFilter() {

+  assert(WarningAsErrorFilter != nullptr);

+  return *WarningAsErrorFilter;

+}

+

+/// \brief Store a \c ClangTidyError.

+void ClangTidyContext::storeError(const ClangTidyError &Error) {

+  Errors.push_back(Error);

+}

+

+StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {

+  llvm::DenseMap<unsigned, std::string>::const_iterator I =

+      CheckNamesByDiagnosticID.find(DiagnosticID);

+  if (I != CheckNamesByDiagnosticID.end())

+    return I->second;

+  return "";

+}

+

+ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)

+    : Context(Ctx), LastErrorRelatesToUserCode(false),

+      LastErrorPassesLineFilter(false), LastErrorWasIgnored(false) {

+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();

+  Diags.reset(new DiagnosticsEngine(

+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,

+      /*ShouldOwnClient=*/false));

+  Context.setDiagnosticsEngine(Diags.get());

+}

+

+void ClangTidyDiagnosticConsumer::finalizeLastError() {

+  if (!Errors.empty()) {

+    ClangTidyError &Error = Errors.back();

+    if (!Context.getChecksFilter().contains(Error.DiagnosticName) &&

+        Error.DiagLevel != ClangTidyError::Error) {

+      ++Context.Stats.ErrorsIgnoredCheckFilter;

+      Errors.pop_back();

+    } else if (!LastErrorRelatesToUserCode) {

+      ++Context.Stats.ErrorsIgnoredNonUserCode;

+      Errors.pop_back();

+    } else if (!LastErrorPassesLineFilter) {

+      ++Context.Stats.ErrorsIgnoredLineFilter;

+      Errors.pop_back();

+    } else {

+      ++Context.Stats.ErrorsDisplayed;

+    }

+  }

+  LastErrorRelatesToUserCode = false;

+  LastErrorPassesLineFilter = false;

+}

+

+static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {

+  bool Invalid;

+  const char *CharacterData = SM.getCharacterData(Loc, &Invalid);

+  if (Invalid)

+    return false;

+

+  // Check if there's a NOLINT on this line.

+  const char *P = CharacterData;

+  while (*P != '\0' && *P != '\r' && *P != '\n')

+    ++P;

+  StringRef RestOfLine(CharacterData, P - CharacterData + 1);

+  // FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.

+  if (RestOfLine.find("NOLINT") != StringRef::npos)

+    return true;

+

+  // Check if there's a NOLINTNEXTLINE on the previous line.

+  const char *BufBegin =

+      SM.getCharacterData(SM.getLocForStartOfFile(SM.getFileID(Loc)), &Invalid);

+  if (Invalid || P == BufBegin)

+    return false;

+

+  // Scan backwards over the current line.

+  P = CharacterData;

+  while (P != BufBegin && *P != '\n')

+    --P;

+

+  // If we reached the begin of the file there is no line before it.

+  if (P == BufBegin)

+    return false;

+

+  // Skip over the newline.

+  --P;

+  const char *LineEnd = P;

+

+  // Now we're on the previous line. Skip to the beginning of it.

+  while (P != BufBegin && *P != '\n')

+    --P;

+

+  RestOfLine = StringRef(P, LineEnd - P + 1);

+  if (RestOfLine.find("NOLINTNEXTLINE") != StringRef::npos)

+    return true;

+

+  return false;

+}

+

+static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM,

+                                          SourceLocation Loc) {

+  while (true) {

+    if (LineIsMarkedWithNOLINT(SM, Loc))

+      return true;

+    if (!Loc.isMacroID())

+      return false;

+    Loc = SM.getImmediateExpansionRange(Loc).first;

+  }

+  return false;

+}

+

+void ClangTidyDiagnosticConsumer::HandleDiagnostic(

+    DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {

+  if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)

+    return;

+

+  if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&

+      DiagLevel != DiagnosticsEngine::Fatal &&

+      LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(),

+                                    Info.getLocation())) {

+    ++Context.Stats.ErrorsIgnoredNOLINT;

+    // Ignored a warning, should ignore related notes as well

+    LastErrorWasIgnored = true;

+    return;

+  }

+

+  LastErrorWasIgnored = false;

+  // Count warnings/errors.

+  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);

+

+  if (DiagLevel == DiagnosticsEngine::Note) {

+    assert(!Errors.empty() &&

+           "A diagnostic note can only be appended to a message.");

+  } else {

+    finalizeLastError();

+    StringRef WarningOption =

+        Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(

+            Info.getID());

+    std::string CheckName = !WarningOption.empty()

+                                ? ("clang-diagnostic-" + WarningOption).str()

+                                : Context.getCheckName(Info.getID()).str();

+

+    if (CheckName.empty()) {

+      // This is a compiler diagnostic without a warning option. Assign check

+      // name based on its level.

+      switch (DiagLevel) {

+      case DiagnosticsEngine::Error:

+      case DiagnosticsEngine::Fatal:

+        CheckName = "clang-diagnostic-error";

+        break;

+      case DiagnosticsEngine::Warning:

+        CheckName = "clang-diagnostic-warning";

+        break;

+      default:

+        CheckName = "clang-diagnostic-unknown";

+        break;

+      }

+    }

+

+    ClangTidyError::Level Level = ClangTidyError::Warning;

+    if (DiagLevel == DiagnosticsEngine::Error ||

+        DiagLevel == DiagnosticsEngine::Fatal) {

+      // Force reporting of Clang errors regardless of filters and non-user

+      // code.

+      Level = ClangTidyError::Error;

+      LastErrorRelatesToUserCode = true;

+      LastErrorPassesLineFilter = true;

+    }

+    bool IsWarningAsError =

+        DiagLevel == DiagnosticsEngine::Warning &&

+        Context.getWarningAsErrorFilter().contains(CheckName);

+    Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),

+                        IsWarningAsError);

+  }

+

+  ClangTidyDiagnosticRenderer Converter(

+      Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),

+      Errors.back());

+  SmallString<100> Message;

+  Info.FormatDiagnostic(Message);

+  SourceManager *Sources = nullptr;

+  if (Info.hasSourceManager())

+    Sources = &Info.getSourceManager();

+  Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message,

+                           Info.getRanges(), Info.getFixItHints(), Sources);

+

+  checkFilters(Info.getLocation());

+}

+

+bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,

+                                                   unsigned LineNumber) const {

+  if (Context.getGlobalOptions().LineFilter.empty())

+    return true;

+  for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {

+    if (FileName.endswith(Filter.Name)) {

+      if (Filter.LineRanges.empty())

+        return true;

+      for (const FileFilter::LineRange &Range : Filter.LineRanges) {

+        if (Range.first <= LineNumber && LineNumber <= Range.second)

+          return true;

+      }

+      return false;

+    }

+  }

+  return false;

+}

+

+void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {

+  // Invalid location may mean a diagnostic in a command line, don't skip these.

+  if (!Location.isValid()) {

+    LastErrorRelatesToUserCode = true;

+    LastErrorPassesLineFilter = true;

+    return;

+  }

+

+  const SourceManager &Sources = Diags->getSourceManager();

+  if (!*Context.getOptions().SystemHeaders &&

+      Sources.isInSystemHeader(Location))

+    return;

+

+  // FIXME: We start with a conservative approach here, but the actual type of

+  // location needed depends on the check (in particular, where this check wants

+  // to apply fixes).

+  FileID FID = Sources.getDecomposedExpansionLoc(Location).first;

+  const FileEntry *File = Sources.getFileEntryForID(FID);

+

+  // -DMACRO definitions on the command line have locations in a virtual buffer

+  // that doesn't have a FileEntry. Don't skip these as well.

+  if (!File) {

+    LastErrorRelatesToUserCode = true;

+    LastErrorPassesLineFilter = true;

+    return;

+  }

+

+  StringRef FileName(File->getName());

+  LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||

+                               Sources.isInMainFile(Location) ||

+                               getHeaderFilter()->match(FileName);

+

+  unsigned LineNumber = Sources.getExpansionLineNumber(Location);

+  LastErrorPassesLineFilter =

+      LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);

+}

+

+llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {

+  if (!HeaderFilter)

+    HeaderFilter.reset(

+        new llvm::Regex(*Context.getOptions().HeaderFilterRegex));

+  return HeaderFilter.get();

+}

+

+void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(

+    SmallVectorImpl<ClangTidyError> &Errors) const {

+  // Each error is modelled as the set of intervals in which it applies

+  // replacements. To detect overlapping replacements, we use a sweep line

+  // algorithm over these sets of intervals.

+  // An event here consists of the opening or closing of an interval. During the

+  // process, we maintain a counter with the amount of open intervals. If we

+  // find an endpoint of an interval and this counter is different from 0, it

+  // means that this interval overlaps with another one, so we set it as

+  // inapplicable.

+  struct Event {

+    // An event can be either the begin or the end of an interval.

+    enum EventType {

+      ET_Begin = 1,

+      ET_End = -1,

+    };

+

+    Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,

+          unsigned ErrorSize)

+        : Type(Type), ErrorId(ErrorId) {

+      // The events are going to be sorted by their position. In case of draw:

+      //

+      // * If an interval ends at the same position at which other interval

+      //   begins, this is not an overlapping, so we want to remove the ending

+      //   interval before adding the starting one: end events have higher

+      //   priority than begin events.

+      //

+      // * If we have several begin points at the same position, we will mark as

+      //   inapplicable the ones that we process later, so the first one has to

+      //   be the one with the latest end point, because this one will contain

+      //   all the other intervals. For the same reason, if we have several end

+      //   points in the same position, the last one has to be the one with the

+      //   earliest begin point. In both cases, we sort non-increasingly by the

+      //   position of the complementary.

+      //

+      // * In case of two equal intervals, the one whose error is bigger can

+      //   potentially contain the other one, so we want to process its begin

+      //   points before and its end points later.

+      //

+      // * Finally, if we have two equal intervals whose errors have the same

+      //   size, none of them will be strictly contained inside the other.

+      //   Sorting by ErrorId will guarantee that the begin point of the first

+      //   one will be processed before, disallowing the second one, and the

+      //   end point of the first one will also be processed before,

+      //   disallowing the first one.

+      if (Type == ET_Begin)

+        Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);

+      else

+        Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);

+    }

+

+    bool operator<(const Event &Other) const {

+      return Priority < Other.Priority;

+    }

+

+    // Determines if this event is the begin or the end of an interval.

+    EventType Type;

+    // The index of the error to which the interval that generated this event

+    // belongs.

+    unsigned ErrorId;

+    // The events will be sorted based on this field.

+    std::tuple<unsigned, EventType, int, int, unsigned> Priority;

+  };

+

+  // Compute error sizes.

+  std::vector<int> Sizes;

+  for (const auto &Error : Errors) {

+    int Size = 0;

+    for (const auto &FileAndReplaces : Error.Fix) {

+      for (const auto &Replace : FileAndReplaces.second)

+        Size += Replace.getLength();

+    }

+    Sizes.push_back(Size);

+  }

+

+  // Build events from error intervals.

+  std::map<std::string, std::vector<Event>> FileEvents;

+  for (unsigned I = 0; I < Errors.size(); ++I) {

+    for (const auto &FileAndReplace : Errors[I].Fix) {

+      for (const auto &Replace : FileAndReplace.second) {

+        unsigned Begin = Replace.getOffset();

+        unsigned End = Begin + Replace.getLength();

+        const std::string &FilePath = Replace.getFilePath();

+        // FIXME: Handle empty intervals, such as those from insertions.

+        if (Begin == End)

+          continue;

+        auto &Events = FileEvents[FilePath];

+        Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);

+        Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);

+      }

+    }

+  }

+

+  std::vector<bool> Apply(Errors.size(), true);

+  for (auto &FileAndEvents : FileEvents) {

+    std::vector<Event> &Events = FileAndEvents.second;

+    // Sweep.

+    std::sort(Events.begin(), Events.end());

+    int OpenIntervals = 0;

+    for (const auto &Event : Events) {

+      if (Event.Type == Event::ET_End)

+        --OpenIntervals;

+      // This has to be checked after removing the interval from the count if it

+      // is an end event, or before adding it if it is a begin event.

+      if (OpenIntervals != 0)

+        Apply[Event.ErrorId] = false;

+      if (Event.Type == Event::ET_Begin)

+        ++OpenIntervals;

+    }

+    assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");

+  }

+

+  for (unsigned I = 0; I < Errors.size(); ++I) {

+    if (!Apply[I]) {

+      Errors[I].Fix.clear();

+      Errors[I].Notes.emplace_back(

+          "this fix will not be applied because it overlaps with another fix");

+    }

+  }

+}

+

+namespace {

+struct LessClangTidyError {

+  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {

+    const tooling::DiagnosticMessage &M1 = LHS.Message;

+    const tooling::DiagnosticMessage &M2 = RHS.Message;

+

+    return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <

+           std::tie(M2.FilePath, M2.FileOffset, M2.Message);

+  }

+};

+struct EqualClangTidyError {

+  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {

+    LessClangTidyError Less;

+    return !Less(LHS, RHS) && !Less(RHS, LHS);

+  }

+};

+} // end anonymous namespace

+

+// Flushes the internal diagnostics buffer to the ClangTidyContext.

+void ClangTidyDiagnosticConsumer::finish() {

+  finalizeLastError();

+

+  std::sort(Errors.begin(), Errors.end(), LessClangTidyError());

+  Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),

+               Errors.end());

+  removeIncompatibleErrors(Errors);

+

+  for (const ClangTidyError &Error : Errors)

+    Context.storeError(Error);

+  Errors.clear();

+}