Index: llvm/include/llvm/Support/FileCheck.h =================================================================== --- llvm/include/llvm/Support/FileCheck.h +++ llvm/include/llvm/Support/FileCheck.h @@ -163,6 +163,8 @@ /// example, there might be a fuzzy match after a fail. enum MatchType { // TODO: More members will appear with later patches in this series. + /// Indicates the final match for an expected pattern. + MatchFinalAndExpected, /// Indicates the final match for an excluded pattern. MatchFinalButExcluded, /// Indicates the final match for an expected pattern, but the match is on Index: llvm/lib/Support/FileCheck.cpp =================================================================== --- llvm/lib/Support/FileCheck.cpp +++ llvm/lib/Support/FileCheck.cpp @@ -416,14 +416,19 @@ const SourceMgr &SM, SMLoc Loc, Check::FileCheckType CheckTy, StringRef Buffer, size_t Pos, size_t Len, - std::vector *Diags) { + std::vector *Diags, + bool AdjustPrevDiag = false) { SMLoc Start = SMLoc::getFromPointer(Buffer.data() + Pos); SMLoc End = SMLoc::getFromPointer(Buffer.data() + Pos + Len); SMRange Range(Start, End); // TODO: The second condition will disappear when we extend this to handle // more match types. - if (Diags && MatchTy != FileCheckDiag::MatchTypeCount) - Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range); + if (Diags && MatchTy != FileCheckDiag::MatchTypeCount) { + if (AdjustPrevDiag) + Diags->rbegin()->MatchTy = MatchTy; + else + Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range); + } return Range; } @@ -905,7 +910,7 @@ return; } SMRange MatchRange = ProcessMatchResult( - ExpectedMatch ? FileCheckDiag::MatchTypeCount + ExpectedMatch ? FileCheckDiag::MatchFinalAndExpected : FileCheckDiag::MatchFinalButExcluded, SM, Loc, Pat.getCheckTy(), Buffer, MatchPos, MatchLen, Diags); std::string Message = formatv("{0}: {1} string found in input", @@ -1060,7 +1065,7 @@ if (CheckNext(SM, SkippedRegion)) { ProcessMatchResult(FileCheckDiag::MatchFinalButWrongLine, SM, Loc, Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen, - Diags); + Diags, Req.Verbose); return StringRef::npos; } @@ -1069,7 +1074,7 @@ if (CheckSame(SM, SkippedRegion)) { ProcessMatchResult(FileCheckDiag::MatchFinalButWrongLine, SM, Loc, Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen, - Diags); + Diags, Req.Verbose); return StringRef::npos; } @@ -1278,6 +1283,8 @@ SM.PrintMessage(OldStart, SourceMgr::DK_Note, "match discarded, overlaps earlier DAG match here", {OldRange}); + if (Diags) + Diags->pop_back(); } MatchPos = MI->End; } Index: llvm/test/FileCheck/dump-input-annotations.txt =================================================================== --- llvm/test/FileCheck/dump-input-annotations.txt +++ llvm/test/FileCheck/dump-input-annotations.txt @@ -21,6 +21,7 @@ ; ALIGN:Full input was: ; ALIGN-NEXT:<<<<<< ; ALIGN-NEXT: 1: hello world +; ALIGN-NEXT:check:1 ^~~~~ ; ALIGN-NEXT:check:2'0 X~~~~ ; ALIGN-NEXT: 2: goodbye ; ALIGN-NEXT:check:2'0 ~~~~~~~ @@ -54,6 +55,7 @@ ; CHK: <<<<<< ; CHK-NEXT: 1: hello +; CHK-V-NEXT: check:1 ^~~~~ ; CHK-NEXT: 2: again ; CHK-NEXT: check:2'0 X~~~~ ; CHK-NEXT: 3: whirled @@ -75,19 +77,22 @@ ; RUN: echo 'CHECK-COUNT-3: pete' > %t.chk ; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk 2>&1 \ -; RUN: | FileCheck -match-full-lines %s -check-prefix=CNT +; RUN: | FileCheck -match-full-lines %s -check-prefixes=CNT,CNT-Q ; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -v 2>&1 \ ; RUN: | FileCheck -match-full-lines %s -check-prefixes=CNT,CNT-V ; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -vv 2>&1 \ ; RUN: | FileCheck -match-full-lines %s -check-prefixes=CNT,CNT-V -; CNT: <<<<<< -; CNT-NEXT: 1: pete -; CNT-NEXT: 2: repete -; CNT-NEXT: 3: repeat -; CNT-NEXT: count:1 X~~~~~ error: no match found -; CNT-NEXT: >>>>>> -; CNT-NOT: {{.}} +; CNT: <<<<<< +; CNT-NEXT: 1: pete +; CNT-V-NEXT: count:1'0 ^~~~ +; CNT-NEXT: 2: repete +; CNT-V-NEXT: count:1'1 ^~~~ +; CNT-NEXT: 3: repeat +; CNT-Q-NEXT: count:1 X~~~~~ error: no match found +; CNT-V-NEXT: count:1'2 X~~~~~ error: no match found +; CNT-NEXT: >>>>>> +; CNT-NOT: {{.}} ;-------------------------------------------------- ; CHECK-NEXT (also: EOF search-range, wrong-line match) @@ -110,10 +115,12 @@ ; RUN: | FileCheck -match-full-lines %s -check-prefixes=NXT,NXT-V,NXT-VV ; NXT: <<<<<< -; NXT-NEXT: 1: hello -; NXT-NEXT: 2: again -; NXT-NEXT: 3: -; NXT-NEXT: next:3 X error: no match found +; NXT-NEXT: 1: hello +; NXT-V-NEXT: check:1 ^~~~~ +; NXT-NEXT: 2: again +; NXT-V-NEXT: next:2 ^~~~~ +; NXT-NEXT: 3: +; NXT-NEXT: next:3 X error: no match found ; NXT-NEXT: >>>>>> ; NXT-NOT: {{.}} @@ -135,7 +142,8 @@ ; NXT2-NOT: {{.}} ;-------------------------------------------------- -; CHECK-SAME (also: single-char search range, wrong-line match) +; CHECK-SAME (also: multiple annotations per line, single-char search range, +; wrong-line match) ;-------------------------------------------------- ; Good match and no match. @@ -154,8 +162,10 @@ ; RUN: | FileCheck -match-full-lines %s -check-prefixes=SAM,SAM-V,SAM-VV ; SAM: <<<<<< -; SAM-NEXT: 1: hello world! -; SAM-NEXT: same:3 X error: no match found +; SAM-NEXT: 1: hello world! +; SAM-V-NEXT: check:1 ^~~~~ +; SAM-V-NEXT: same:2 ^~~~~ +; SAM-NEXT: same:3 X error: no match found ; SAM-NEXT: >>>>>> ; SAM-NOT: {{.}} @@ -168,13 +178,16 @@ ; SAM2: <<<<<< ; SAM2-NEXT: 1: hello world! +; SAM2-NEXT: check:1 ^~~~~ +; SAM2-NEXT: same:2 ^~~~~ ; SAM2-NEXT: 2: again ; SAM2-NEXT: same:3 !~~~~ error: match on wrong line ; SAM2-NEXT: >>>>>> ; SAM2-NOT: {{.}} ;-------------------------------------------------- -; CHECK-EMPTY (also: search range ends at label, wrong-line match) +; CHECK-EMPTY (also: search range ends at label, single-char match, wrong-line +; match) ;-------------------------------------------------- ; Good match and no match. @@ -203,11 +216,14 @@ ; EMP: <<<<<< ; EMP-NEXT: 1: hello +; EMP-V-NEXT: check:1 ^~~~~ ; EMP-NEXT: 2: +; EMP-V-NEXT: empty:2 ^ ; EMP-NEXT: 3: world ; EMP-NEXT: empty:3 X~~~~ ; EMP-NEXT: 4: label ; EMP-NEXT: empty:3 ~~~~~ error: no match found +; EMP-V-NEXT: label:4 ^~~~~ ; EMP-NEXT: >>>>>> ; EMP-NOT: {{.}} @@ -228,6 +244,7 @@ ; EMP2: <<<<<< ; EMP2-NEXT: 1: hello +; EMP2-V-NEXT: check:1 ^~~~~ ; EMP2-NEXT: 2: world ; EMP2-NEXT: 3: ; EMP2-NEXT: empty:2 ! error: match on wrong line @@ -235,7 +252,7 @@ ; EMP2-NOT: {{.}} ;-------------------------------------------------- -; CHECK-NOT +; CHECK-NOT (also: EOF pattern) ;-------------------------------------------------- ; No match (success) and unexpected match (error). @@ -259,6 +276,8 @@ ; NOT-NEXT: 2: world ; NOT-NEXT: not:2 !~~~~ error: no match expected ; NOT-NEXT: 3: again +; NOT-VV-NEXT: 4: +; NOT-VV-NEXT: eof:2 ^ ; NOT-NEXT: >>>>>> ; NOT-NOT: {{.}} @@ -278,11 +297,12 @@ ; NOT2-NEXT: 2: world ; NOT2-NEXT: not:2 !~~~~ error: no match expected ; NOT2-NEXT: 3: again +; NOT2-V-NEXT: check:3 ^~~ ; NOT2-NEXT: >>>>>> ; NOT2-NOT: {{.}} ;-------------------------------------------------- -; CHECK-DAG +; CHECK-DAG (also: matches in different order than directives) ;-------------------------------------------------- ; Good match, discarded match plus good match, and no match. @@ -297,22 +317,28 @@ ; RUN: echo 'CHECK-DAG: def' >> %t.chk ; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk 2>&1 \ -; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG +; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG,DAG-Q ; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -v 2>&1 \ -; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG +; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG,DAG-V ; RUN: not FileCheck -dump-input=always -input-file %t.in %t.chk -vv 2>&1 \ -; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG +; RUN: | FileCheck -match-full-lines %s -check-prefixes=DAG,DAG-V,DAG-VV ; DAG: <<<<<< -; DAG-NEXT: 1: abc -; DAG-NEXT: 2: def -; DAG-NEXT: 3: abc -; DAG-NEXT: dag:4 X~~ error: no match found +; DAG-NEXT: 1: abc +; DAG-V-NEXT: dag:2 ^~~ +; DAG-NEXT: 2: def +; DAG-V-NEXT: dag:1 ^~~ +; DAG-NEXT: 3: abc +; DAG-V-NEXT: dag:3 ^~~ +; DAG-NEXT: dag:4 X~~ error: no match found ; DAG-NEXT: >>>>>> ; DAG-NOT: {{.}} ;-------------------------------------------------- ; CHECK-LABEL +; +; FIXME: Labels sometimes produce redundant diagnostics for good matches. +; That bug is independent of but affects -dump-input. ;-------------------------------------------------- ; Good match and no match. @@ -335,6 +361,8 @@ ; LAB: <<<<<< ; LAB-NEXT: 1: lab0 +; LAB-V-NEXT: label:1'0 ^~~~ +; LAB-V-NEXT: label:1'1 ^~~~ ; LAB-NEXT: 2: foo ; LAB-NEXT: label:3'0 X~~ ; LAB-NEXT: 3: lab1 Index: llvm/test/FileCheck/dump-input-enable.txt =================================================================== --- llvm/test/FileCheck/dump-input-enable.txt +++ llvm/test/FileCheck/dump-input-enable.txt @@ -112,7 +112,9 @@ ; CHECK-GOOD: Full input was: ; CHECK-GOOD-NEXT: <<<<<< ; CHECK-GOOD-NEXT: 1: ciao +; CHECK-GOOD-NEXT: check:1 ^~~~ ; CHECK-GOOD-NEXT: 2: world +; CHECK-GOOD-NEXT: next:2 ^~~~~ ; CHECK-GOOD-NEXT: >>>>>> ; CHECK-ERR: Full input was: Index: llvm/utils/FileCheck/FileCheck.cpp =================================================================== --- llvm/utils/FileCheck/FileCheck.cpp +++ llvm/utils/FileCheck/FileCheck.cpp @@ -137,12 +137,15 @@ /// A note to follow the marker, or empty string if none. std::string Note; MarkerStyle() {} - MarkerStyle(char Lead, raw_ostream::Colors Color, const std::string &Note) + MarkerStyle(char Lead, raw_ostream::Colors Color, + const std::string &Note = "") : Lead(Lead), Color(Color), Note(Note) {} }; static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { switch (MatchTy) { + case FileCheckDiag::MatchFinalAndExpected: + return MarkerStyle('^', raw_ostream::GREEN); case FileCheckDiag::MatchFinalButExcluded: return MarkerStyle('!', raw_ostream::RED, "error: no match expected"); case FileCheckDiag::MatchFinalButWrongLine: @@ -181,6 +184,9 @@ // Markers on annotation lines. OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~"; + OS << " marks good match (reported if -v)\n" + << " - "; WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~"; OS << " marks bad match, such as:\n" << " - CHECK-NEXT on same line as previous match (error)\n" @@ -195,9 +201,13 @@ // Colors. OS << " - colors "; + WithColor(OS, raw_ostream::GREEN, true) << "success"; + OS << ", "; WithColor(OS, raw_ostream::RED, true) << "error"; OS << ", "; WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match"; + OS << ", "; + WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input"; OS << "\n\n" << "If you are not seeing color above or in input dumps, try: -color\n"; } @@ -222,6 +232,8 @@ unsigned InputStartCol, InputEndCol; /// The marker to use. MarkerStyle Marker; + /// Whether this annotation represents a final match for an expected pattern. + bool FinalAndExpectedMatch; }; /// Get an abbreviation for the check type. @@ -289,6 +301,8 @@ MarkerStyle Marker = GetMarker(DiagItr->MatchTy); A.Marker = Marker; + A.FinalAndExpectedMatch = + DiagItr->MatchTy == FileCheckDiag::MatchFinalAndExpected; // Compute the mark location, and break annotation into multiple // annotations if it spans multiple lines. @@ -328,15 +342,17 @@ B.Marker.Note = ""; } else B.InputEndCol = DiagItr->InputEndCol; + B.FinalAndExpectedMatch = A.FinalAndExpectedMatch; Annotations.push_back(B); } } } } -static void DumpAnnotatedInput( - raw_ostream &OS, StringRef InputFileText, - std::vector &Annotations, unsigned LabelWidth) { +static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, + StringRef InputFileText, + std::vector &Annotations, + unsigned LabelWidth) { OS << "Full input was:\n<<<<<<\n"; // Sort annotations. @@ -359,9 +375,15 @@ return A.InputLine < B.InputLine; if (A.CheckLine != B.CheckLine) return A.CheckLine < B.CheckLine; - assert(A.CheckDiagIndex != B.CheckDiagIndex && - "expected diagnostic indices to be unique within a " - " check line"); + // FIXME: Sometimes CHECK-LABEL reports its match twice with + // other diagnostics in between, and then diag index incrementing + // fails to work properly, and then this assert fails. We should + // suppress one of those diagnostics or do a better job of + // computing this index. For now, we just produce a redundant + // CHECK-LABEL annotation. + // assert(A.CheckDiagIndex != B.CheckDiagIndex && + // "expected diagnostic indices to be unique within a " + // " check line"); return A.CheckDiagIndex < B.CheckDiagIndex; }); @@ -393,14 +415,45 @@ WithColor(OS, raw_ostream::BLACK, true) << format_decimal(Line, LabelWidth) << ": "; - // Print numbered line. + // For case where -v and colors are enabled, find the annotations for final + // matches for expected patterns in order to highlight everything else in + // the line. There are no such annotations if -v is disabled. + std::vector FinalAndExpectedMatches; + if (Req.Verbose && WithColor(OS).colorsEnabled()) { + for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; + ++I) { + if (I->FinalAndExpectedMatch) + FinalAndExpectedMatches.push_back(*I); + } + } + + // Print numbered line with highlighting where there are no matches for + // expected patterns. bool Newline = false; - while (InputFilePtr != InputFileEnd && !Newline) { - if (*InputFilePtr == '\n') - Newline = true; - else - OS << *InputFilePtr; - ++InputFilePtr; + { + WithColor COS(OS); + bool InMatch = false; + if (Req.Verbose) + COS.changeColor(raw_ostream::CYAN, true, true); + for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) { + bool WasInMatch = InMatch; + InMatch = false; + for (auto M : FinalAndExpectedMatches) { + if (M.InputStartCol <= Col && Col < M.InputEndCol) { + InMatch = true; + break; + } + } + if (!WasInMatch && InMatch) + COS.resetColor(); + else if (WasInMatch && !InMatch) + COS.changeColor(raw_ostream::CYAN, true, true); + if (*InputFilePtr == '\n') + Newline = true; + else + COS << *InputFilePtr; + ++InputFilePtr; + } } OS << '\n'; unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; @@ -561,7 +614,7 @@ std::vector Annotations; unsigned LabelWidth; BuildInputAnnotations(Diags, Annotations, LabelWidth); - DumpAnnotatedInput(errs(), InputFileText, Annotations, LabelWidth); + DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); } return ExitCode;