Index: llvm/include/llvm/Support/FileCheck.h =================================================================== --- llvm/include/llvm/Support/FileCheck.h +++ llvm/include/llvm/Support/FileCheck.h @@ -164,6 +164,7 @@ /// example, there might be a fuzzy match after a fail. enum MatchType { // TODO: More members will appear with later patches in this series. + MatchFinalAndExpected, //< the final match for an expected pattern MatchFinalButExcluded, //< the final match for an excluded pattern MatchFinalButIllegal, //< the final but illegal match for an expected pattern MatchNoneButExpected, //< no match for an expected pattern 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::MatchFinalButIllegal, SM, Loc, Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen, - Diags); + Diags, Req.Verbose); return StringRef::npos; } @@ -1069,7 +1074,7 @@ if (CheckSame(SM, SkippedRegion)) { ProcessMatchResult(FileCheckDiag::MatchFinalButIllegal, 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 @@ -20,6 +20,7 @@ ; ALIGN:Full input was: ; ALIGN-NEXT:<<<<<< ; ALIGN-NEXT: 1: hello world +; ALIGN-NEXT:check:1 ^~~~~ ; ALIGN-NEXT:check:2 X~~~~ ; ALIGN-NEXT: 2: goodbye ; ALIGN-NEXT:check:2 ~~~~~~~ @@ -50,6 +51,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 @@ -71,19 +73,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~~~~~ -; 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~~~~~ +; CNT-V-NEXT: count:1'2 X~~~~~ +; CNT-NEXT: >>>>>> +; CNT-NOT: {{.}} ;-------------------------------------------------- ; CHECK-NEXT (also: EOF search-range, illegal match) @@ -106,10 +111,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 +; 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 ; NXT-NEXT: >>>>>> ; NXT-NOT: {{.}} @@ -131,7 +138,8 @@ ; NXT2-NOT: {{.}} ;-------------------------------------------------- -; CHECK-SAME (also: single-char search range, illegal match) +; CHECK-SAME (also: multiple annotations per line, single-char search range, +; illegal match) ;-------------------------------------------------- ; Good match and no match. @@ -150,8 +158,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 +; SAM-NEXT: 1: hello world! +; SAM-V-NEXT: check:1 ^~~~~ +; SAM-V-NEXT: same:2 ^~~~~ +; SAM-NEXT: same:3 X ; SAM-NEXT: >>>>>> ; SAM-NOT: {{.}} @@ -164,13 +174,16 @@ ; SAM2: <<<<<< ; SAM2-NEXT: 1: hello world! +; SAM2-NEXT: check:1 ^~~~~ +; SAM2-NEXT: same:2 ^~~~~ ; SAM2-NEXT: 2: again ; SAM2-NEXT: same:3 !~~~~ ; SAM2-NEXT: >>>>>> ; SAM2-NOT: {{.}} ;-------------------------------------------------- -; CHECK-EMPTY (also: search range ends at label, illegal match) +; CHECK-EMPTY (also: search range ends at label, single-char match, illegal +; match) ;-------------------------------------------------- ; Good match and no match. @@ -199,11 +212,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 ~~~~~ +; EMP-V-NEXT: label:4 ^~~~~ ; EMP-NEXT: >>>>>> ; EMP-NOT: {{.}} @@ -224,6 +240,7 @@ ; EMP2: <<<<<< ; EMP2-NEXT: 1: hello +; EMP2-V-NEXT: check:1 ^~~~~ ; EMP2-NEXT: 2: world ; EMP2-NEXT: 3: ; EMP2-NEXT: empty:2 ! @@ -231,7 +248,7 @@ ; EMP2-NOT: {{.}} ;-------------------------------------------------- -; CHECK-NOT +; CHECK-NOT (also: EOF pattern) ;-------------------------------------------------- ; No match (success) and illegal match. @@ -255,6 +272,8 @@ ; NOT-NEXT: 2: world ; NOT-NEXT: not:2 !~~~~ ; NOT-NEXT: 3: again +; NOT-VV-NEXT: 4: +; NOT-VV-NEXT: eof:2 ^ ; NOT-NEXT: >>>>>> ; NOT-NOT: {{.}} @@ -274,11 +293,12 @@ ; NOT2-NEXT: 2: world ; NOT2-NEXT: not:2 !~~~~ ; 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. @@ -293,22 +313,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~~ +; 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~~ ; 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. @@ -331,6 +357,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 @@ -138,6 +138,8 @@ static MatchTypeStyle GetMatchTypeStyle(unsigned MatchTy) { switch (MatchTy) { + case FileCheckDiag::MatchFinalAndExpected: + return MatchTypeStyle('^', raw_ostream::GREEN); case FileCheckDiag::MatchFinalButExcluded: return MatchTypeStyle('!', raw_ostream::RED); case FileCheckDiag::MatchFinalButIllegal: @@ -174,6 +176,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" @@ -188,9 +193,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"; } @@ -215,6 +224,8 @@ unsigned InputStartCol, InputEndCol; /// The starting char (before tildes) for marking the line. char Mark; + /// Whether this annotation represents a final match for an expected pattern. + bool FinalAndExpectedMatch; /// What color to use for this annotation. raw_ostream::Colors Color; }; @@ -285,6 +296,8 @@ MatchTypeStyle MatchTyStyle = GetMatchTypeStyle(DiagItr->MatchTy); A.Mark = MatchTyStyle.Mark; A.Color = MatchTyStyle.Color; + A.FinalAndExpectedMatch = + DiagItr->MatchTy == FileCheckDiag::MatchFinalAndExpected; // Compute the mark location, and break annotation into multiple // annotations if it spans multiple lines. @@ -319,6 +332,7 @@ B.InputEndCol = UINT_MAX; else B.InputEndCol = DiagItr->InputEndCol; + B.FinalAndExpectedMatch = A.FinalAndExpectedMatch; B.Color = A.Color; Annotations.push_back(B); } @@ -326,9 +340,10 @@ } } -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. @@ -351,9 +366,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; }); @@ -385,14 +406,44 @@ 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::list 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); + if (Req.Verbose) + COS.changeColor(raw_ostream::CYAN, true, true); + for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) { + bool StartsMatch = false; + bool EndsMatch = false; + for (auto M : FinalAndExpectedMatches) { + if (Col == M.InputEndCol) + EndsMatch = true; + else if (Col == M.InputStartCol) + StartsMatch = true; + } + if (StartsMatch) + COS.resetColor(); + else if (EndsMatch) + COS.changeColor(raw_ostream::CYAN, true, true); + if (*InputFilePtr == '\n') + Newline = true; + else + COS << *InputFilePtr; + ++InputFilePtr; + } } OS << '\n'; unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; @@ -543,7 +594,7 @@ std::vector Annotations; unsigned LabelWidth; BuildInputAnnotations(Diags, Annotations, LabelWidth); - DumpAnnotatedInput(errs(), InputFileText, Annotations, LabelWidth); + DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); } return ExitCode;