================
@@ -1044,6 +1145,141 @@ static unsigned CheckResults(DiagnosticsEngine &Diags, 
SourceManager &SourceMgr,
   return NumProblems;
 }
 
+// Checks that directives are lexically in the same order as the emitted
+// diagnostics. Assumes that:
+//   - every directive matches exactly one diagnostic,
+//   - there are no wildcards, and
+//   - CheckResults returned 0 problems, i.e. every diagnostic
+//     was matched by every directive without considering the order.
+static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
+                                       SourceManager &SourceMgr,
+                                       const TextDiagnosticBuffer &Buffer,
+                                       const ExpectedData &ED) {
+  // Building a set of all directives ordered by their location
+  auto directiveComparator = [](const Directive *LHS, const Directive *RHS) {
+    return LHS->DirectiveLoc < RHS->DirectiveLoc;
+  };
+  auto sortDirectives = [&](const DirectiveList &Unordered) {
+    std::vector<const Directive *> Ordered(Unordered.size());
+    std::transform(Unordered.cbegin(), Unordered.cend(), Ordered.begin(),
+                   [](const std::unique_ptr<Directive> &D) { return &*D; });
+    std::sort(Ordered.begin(), Ordered.end(), directiveComparator);
+    return Ordered;
+  };
+  std::vector<const Directive *> OrderedErrors = sortDirectives(ED.Errors);
+  std::vector<const Directive *> OrderedWarns = sortDirectives(ED.Warnings);
+  std::vector<const Directive *> OrderedNotes = sortDirectives(ED.Notes);
+  std::vector<const Directive *> OrderedRemarks = sortDirectives(ED.Remarks);
+
+  std::vector<const Directive *> OrderedDirectives = [&] {
+    std::vector<const Directive *> OrderedEW(OrderedErrors.size() +
+                                             OrderedWarns.size());
+    std::merge(OrderedErrors.cbegin(), OrderedErrors.cend(),
+               OrderedWarns.cbegin(), OrderedWarns.cend(), OrderedEW.begin(),
+               directiveComparator);
+
+    std::vector<const Directive *> OrderedNR(OrderedNotes.size() +
+                                             OrderedRemarks.size());
+    std::merge(OrderedNotes.cbegin(), OrderedNotes.cend(),
+               OrderedRemarks.cbegin(), OrderedRemarks.cend(),
+               OrderedNR.begin(), directiveComparator);
+
+    std::vector<const Directive *> OrderedDirectives(OrderedEW.size() +
+                                                     OrderedNR.size());
+    std::merge(OrderedEW.cbegin(), OrderedEW.cend(), OrderedNR.cbegin(),
+               OrderedNR.cend(), OrderedDirectives.begin(),
+               directiveComparator);
+    return OrderedDirectives;
+  }();
+
+  auto getLocDiagPair = [&](DiagnosticsEngine::Level DiagLevel, long DiagIndex)
+      -> const std::pair<clang::SourceLocation, std::basic_string<char>> & {
+    TextDiagnosticBuffer::const_iterator It = [&] {
+      switch (DiagLevel) {
+      case DiagnosticsEngine::Level::Fatal:
+      case DiagnosticsEngine::Level::Error:
+        assert(DiagIndex < Buffer.err_end() - Buffer.err_begin() &&
+               "DiagIndex is out of bounds!");
+        return Buffer.err_begin();
+      case DiagnosticsEngine::Level::Warning:
+        assert(DiagIndex < Buffer.warn_end() - Buffer.warn_begin() &&
+               "DiagIndex is out of bounds!");
+        return Buffer.warn_begin();
+      case DiagnosticsEngine::Level::Note:
+        assert(DiagIndex < Buffer.note_end() - Buffer.note_begin() &&
+               "DiagIndex is out of bounds!");
+        return Buffer.note_begin();
+      case DiagnosticsEngine::Level::Remark:
+        assert(DiagIndex < Buffer.remark_end() - Buffer.remark_begin() &&
+               "DiagIndex is out of bounds!");
+        return Buffer.remark_begin();
+      case DiagnosticsEngine::Level::Ignored:
+        llvm_unreachable("Unexpected diagnostic level!");
+      }
+    }();
+
+    std::advance(It, DiagIndex);
+    return *It;
+  };
+
+  using LevelDiagPairT = std::pair<DiagnosticsEngine::Level, size_t>;
+  static_assert(std::is_same_v<LevelDiagPairT,
+                               TextDiagnosticBuffer::AllDiagList::value_type>);
+  int NumProblems = 0;
+  SmallString<256> Fmt;
+  llvm::raw_svector_ostream OS(Fmt);
+  // zip_equal asserts that there're as many directives as emitted diagnostics.
+  // CheckResults has already ensured that all diagnostics were matched.
+  for (const auto [Directive, LevelDiagPair] : llvm::zip_equal(
+           OrderedDirectives,
+           llvm::iterator_range{Buffer.all_begin(), Buffer.all_end()})) {
+    assert(!Directive->MatchAnyFileAndLine && !Directive->MatchAnyLine &&
+           "Cannot compare source locations when wildcards are present");
+    const auto [DiagLevel, DiagIndex] = LevelDiagPair;
+    const auto &[DiagLoc, DiagText] = getLocDiagPair(DiagLevel, DiagIndex);
+    const SourceLocation DirLoc = Directive->DirectiveLoc;
+
+    bool LocsMatch =
+        SourceMgr.getPresumedLineNumber(DiagLoc) ==
+            SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc) &&
+        IsFromSameFile(SourceMgr, Directive->DiagnosticLoc, DiagLoc);
+    bool TextMatch = Directive->match(DiagText) == DiagnosticMatchResult::Full;
+    if (LocsMatch && TextMatch) {
+      continue;
+    }
+    ++NumProblems;
+
+    auto printFileNameIfDifferent = [&](SourceLocation DirLoc,
+                                        SourceLocation Loc) {
+      if (!IsFromSameFile(SourceMgr, DirLoc, Loc)) {
+        OS << " in " << SourceMgr.getFilename(Loc);
+      }
+    };
+
+    OS << "\n  '" << Directive->Spelling << "' at line "
+       << SourceMgr.getPresumedLineNumber(DirLoc) << " in "
+       << SourceMgr.getFilename(DirLoc) << ": " << Directive->Text
+       << "\n    matches diagnostic at line "
+       << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
+    printFileNameIfDifferent(DirLoc, Directive->DiagnosticLoc);
+    if (TextMatch) {
+      OS << ", but diagnostic with the same message was first emitted at line "
+         << SourceMgr.getPresumedLineNumber(DiagLoc);
+      printFileNameIfDifferent(DirLoc, DiagLoc);
+    } else {
+      OS << ", but diagnostic at line "
----------------
AaronBallman wrote:

It would be nice to emit these as actual notes so we can associate them with a 
line directly instead of making the user figure out where in the source to go 
look (particularly because we muck with line numbers in tests via line 
directives). However, I think we can burn that bridge when we come to it, so 
nothing to be done for this PR.

https://github.com/llvm/llvm-project/pull/179835
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to