================
@@ -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