[llvm] [FileCheck] Add a diff output option for FileCheck (PR #187120)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Sat Apr 18 12:51:50 PDT 2026
================
@@ -2724,9 +2731,168 @@ void FileCheckPatternContext::clearLocalVars() {
GlobalNumericVariableTable.erase(Var);
}
+struct DiffContext {
+ StringRef Line;
+ StringRef LineBefore;
+ StringRef LineAfter;
+};
+
+// Provides the "surrounding context" for diff output.
+static DiffContext getDiffContext(SourceMgr &SM, unsigned LineNo,
+ unsigned BufID) {
+ const MemoryBuffer *Buffer = SM.getMemoryBuffer(BufID);
+ StringRef BufText = Buffer->getBuffer();
+
+ auto getLineText = [&](unsigned L) -> StringRef {
+ if (L == 0)
+ return "";
+
+ SMLoc LineLoc = SM.FindLocForLineAndColumn(BufID, L, 1);
+ if (!LineLoc.isValid())
+ return "";
+
+ StringRef FromLineStart(LineLoc.getPointer(),
+ BufText.end() - LineLoc.getPointer());
+ return FromLineStart
+ .take_while([](char C) { return C != '\n' && C != '\r'; })
+ .trim();
+ };
+
+ return {getLineText(LineNo), getLineText(LineNo - 1),
+ getLineText(LineNo + 1)};
+}
+
+// Renders a diagnostic diff via llvm::errs().
+static void renderDiff(unsigned ExpectedLineNo, unsigned ActualLineNo,
+ StringRef ExpectedLine, StringRef ActualLine,
+ const DiffContext &Ctx) {
+ auto &OS = errs();
+
+ // Header
+ OS.changeColor(raw_ostream::CYAN);
+ OS << "@@ -" << ExpectedLineNo << " +" << ActualLineNo << " @@\n";
+ OS.resetColor();
+
+ // Before Context
+ if (!Ctx.LineBefore.empty()) {
+ OS << " " << Ctx.LineBefore << "\n";
+ }
+
+ // Mismatch
+ OS.changeColor(raw_ostream::RED);
+ OS << "-" << ExpectedLine << "\n";
+
+ OS.changeColor(raw_ostream::GREEN);
+ OS << "+" << ActualLine.ltrim() << "\n";
+ OS.resetColor();
+
+ // After Context
+ if (!Ctx.LineAfter.empty()) {
+ OS << " " << Ctx.LineAfter << "\n";
+ }
+}
+
+static bool printDiff(const FileCheckString &CheckStr, StringRef ActualLine,
+ SourceMgr &SM, std::vector<FileCheckDiag> *Diags,
+ unsigned OverwriteActualLine = 0) {
+ SMLoc PatternLoc = CheckStr.Pat.getLoc();
+ unsigned ExpectedLineNo = SM.getLineAndColumn(PatternLoc).first;
+ const char *PatPtr = PatternLoc.getPointer();
+ StringRef ExpectedLine = StringRef(PatPtr).split('\n').first.rtrim();
+
+ // Resolve the Actual (Input) line number.
+ // Priority: 1. OverwriteActualLine (Found via Fuzzy match)
+ // 2. Direct pointer resolution via SourceMgr.
+ SMLoc InputLoc = SMLoc::getFromPointer(ActualLine.data());
+
+ unsigned ActualLineNo = OverwriteActualLine;
+
+ // If no Fuzzy match was found, calculate the line number directly
+ // from the InputLoc pointer using the SourceManager.
+ if (ActualLineNo == 0)
+ ActualLineNo = SM.getLineAndColumn(InputLoc).first;
+
+ // if we are at an empty line (and not from fuzzy), usually the relevant
+ // context is the line just before it.
+ if (ActualLine.empty() && ActualLineNo > 1)
+ ActualLineNo--;
+
+ unsigned BufID = SM.FindBufferContainingLoc(InputLoc);
+ DiffContext Context = getDiffContext(SM, ActualLineNo, BufID);
+
+ renderDiff(ExpectedLineNo, ActualLineNo, ExpectedLine, ActualLine, Context);
+
+ errs() << '\n';
+ return true;
+}
+
+// Report the mismatch on the current line and advance to the next line.
+static bool handleDiffFailure(const FileCheckString &CheckStr,
+ StringRef &CheckRegion, SourceMgr &SM,
+ std::vector<FileCheckDiag> *Diags,
+ raw_ostream &OS, bool &HeaderPrinted,
+ unsigned &TotalMismatches) {
+ // Print headers once per CheckRegion.
+ if (!HeaderPrinted) {
+ StringRef CheckFile =
+ SM.getMemoryBuffer(SM.getMainFileID())->getBufferIdentifier();
+ unsigned InputBufID =
+ SM.FindBufferContainingLoc(SMLoc::getFromPointer(CheckRegion.data()));
+ StringRef InputFile = SM.getMemoryBuffer(InputBufID)->getBufferIdentifier();
+
+ OS.changeColor(raw_ostream::WHITE, true);
+ OS << "--- " << CheckFile << "\n";
+ OS << "+++ " << InputFile << "\n";
+ OS.resetColor();
+ HeaderPrinted = true;
+ }
+
+ size_t EOL = CheckRegion.find('\n');
+
+ SMLoc CurrentLoc = SMLoc::getFromPointer(CheckRegion.data());
+ StringRef TargetLine;
+ unsigned TargetLineNo = 0;
+
+ // Check if the existing diagnostics already found a fuzzy match.
+ if (Diags) {
+ for (const auto &D : llvm::reverse(*Diags)) {
+ if (D.CheckLoc == CheckStr.Pat.getLoc() &&
+ D.MatchTy == FileCheckDiag::MatchFuzzy) {
+ TargetLineNo = D.InputStartLine;
+ // Get the actual text of that fuzzy match from the SourceMgr
+ unsigned BufID = SM.FindBufferContainingLoc(CurrentLoc);
+ SMLoc FuzzyLoc = SM.FindLocForLineAndColumn(BufID, TargetLineNo, 1);
+ TargetLine = StringRef(FuzzyLoc.getPointer()).split('\n').first;
+ break;
+ }
+ }
+ }
+
+ // If no fuzzy match was found by the engine, just use the next line.
+ if (TargetLine.empty()) {
+ TargetLine = CheckRegion.substr(0, EOL);
+ TargetLineNo = SM.getLineAndColumn(CurrentLoc).first;
+ }
+
+ printDiff(CheckStr, TargetLine, SM, Diags, TargetLineNo);
+ TotalMismatches++;
----------------
MaskRay wrote:
https://llvm.org/docs/CodingStandards.html#prefer-preincrement
https://github.com/llvm/llvm-project/pull/187120
More information about the llvm-commits
mailing list