[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