1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-26 12:43:36 +01:00

[FileCheck] introduce CHECK-COUNT-<num> repetition directive

In some cases it is desirable to match the same pattern repeatedly
many times. Currently the only way to do it is to copy the same
check pattern as many times as needed. And that gets pretty unwieldy
when its more than count is big.

Introducing CHECK-COUNT-<num> directive which acts like a plain CHECK
directive yet matches the same pattern exactly <num> times.

Extended FileCheckType to a struct to add Count there.
Changed some parsing routines to handle non-fixed length of directive
(all currently existing directives were fixed-length).

The code is generic enough to allow future support for COUNT in more
than just PlainCheck directives.

See motivating example for this feature in reviews.llvm.org/D54223.

Reviewed By: chandlerc, dblaikie
Differential Revision: https://reviews.llvm.org/D54336

llvm-svn: 346722
This commit is contained in:
Fedor Sergeev 2018-11-13 00:46:13 +00:00
parent 184774cd2c
commit 82854f2e28
4 changed files with 293 additions and 105 deletions

View File

@ -311,6 +311,29 @@ can be used:
; CHECK: ret i8
}
The "CHECK-COUNT:" directive
~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you need to match multiple lines with the same pattern over and over again
you can repeat a plain ``CHECK:`` as many times as needed. If that looks too
boring you can instead use a counted check "``CHECK-COUNT-<num>:``", where
``<num>`` is a positive decimal number. It will match the pattern exactly
``<num>`` times, no more and no less. If you specified a custom check prefix,
just use "``<PREFIX>-COUNT-<num>:``" for the same effect.
Here is a simple example:
.. code-block:: llvm
Loop at depth 1
Loop at depth 1
Loop at depth 1
Loop at depth 1
Loop at depth 2
Loop at depth 3
; CHECK-COUNT-6: Loop at depth {{[0-9]+}}
; CHECK-NOT: Loop at depth {{[0-9]+}}
The "CHECK-DAG:" directive
~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -43,7 +43,8 @@ struct FileCheckRequest {
//===----------------------------------------------------------------------===//
namespace Check {
enum FileCheckType {
enum FileCheckKind {
CheckNone = 0,
CheckPlain,
CheckNext,
@ -58,7 +59,26 @@ enum FileCheckType {
CheckEOF,
/// Marks when parsing found a -NOT check combined with another CHECK suffix.
CheckBadNot
CheckBadNot,
/// Marks when parsing found a -COUNT directive with invalid count value.
CheckBadCount
};
class FileCheckType {
FileCheckKind Kind;
int Count; //< optional Count for some checks
public:
FileCheckType(FileCheckKind Kind = CheckNone) : Kind(Kind), Count(1) {}
FileCheckType(const FileCheckType &) = default;
operator FileCheckKind() const { return Kind; }
int getCount() const { return Count; }
FileCheckType &setCount(int C);
std::string getDescription(StringRef Prefix) const;
};
}
@ -113,6 +133,8 @@ public:
Check::FileCheckType getCheckTy() const { return CheckTy; }
int getCount() const { return CheckTy.getCount(); }
private:
bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM);
void AddBackrefToRegEx(unsigned BackrefNum);

View File

@ -16,8 +16,12 @@
#include "llvm/Support/FileCheck.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/FormatVariadic.h"
#include <cstdint>
#include <list>
#include <map>
#include <tuple>
#include <utility>
using namespace llvm;
@ -531,47 +535,22 @@ static bool IsPartOfWord(char c) {
return (isalnum(c) || c == '-' || c == '_');
}
// Get the size of the prefix extension.
static size_t CheckTypeSize(Check::FileCheckType Ty) {
switch (Ty) {
case Check::CheckNone:
case Check::CheckBadNot:
return 0;
case Check::CheckPlain:
return sizeof(":") - 1;
case Check::CheckNext:
return sizeof("-NEXT:") - 1;
case Check::CheckSame:
return sizeof("-SAME:") - 1;
case Check::CheckNot:
return sizeof("-NOT:") - 1;
case Check::CheckDAG:
return sizeof("-DAG:") - 1;
case Check::CheckLabel:
return sizeof("-LABEL:") - 1;
case Check::CheckEmpty:
return sizeof("-EMPTY:") - 1;
case Check::CheckEOF:
llvm_unreachable("Should not be using EOF size");
}
llvm_unreachable("Bad check type");
Check::FileCheckType &Check::FileCheckType::setCount(int C) {
assert(Count > 0 || "zero and negative counts are not supported");
assert((C == 1 || Kind == CheckPlain) &&
"count supported only for plain CHECK directives");
Count = C;
return *this;
}
// Get a description of the type.
static std::string CheckTypeName(StringRef Prefix, Check::FileCheckType Ty) {
switch (Ty) {
std::string Check::FileCheckType::getDescription(StringRef Prefix) const {
switch (Kind) {
case Check::CheckNone:
return "invalid";
case Check::CheckPlain:
if (Count > 1)
return Prefix.str() + "-COUNT";
return Prefix;
case Check::CheckNext:
return Prefix.str() + "-NEXT";
@ -589,50 +568,65 @@ static std::string CheckTypeName(StringRef Prefix, Check::FileCheckType Ty) {
return "implicit EOF";
case Check::CheckBadNot:
return "bad NOT";
case Check::CheckBadCount:
return "bad COUNT";
}
llvm_unreachable("unknown FileCheckType");
}
static Check::FileCheckType FindCheckType(StringRef Buffer, StringRef Prefix) {
static std::pair<Check::FileCheckType, StringRef>
FindCheckType(StringRef Buffer, StringRef Prefix) {
if (Buffer.size() <= Prefix.size())
return Check::CheckNone;
return {Check::CheckNone, StringRef()};
char NextChar = Buffer[Prefix.size()];
StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
// Verify that the : is present after the prefix.
if (NextChar == ':')
return Check::CheckPlain;
return {Check::CheckPlain, Rest};
if (NextChar != '-')
return Check::CheckNone;
return {Check::CheckNone, StringRef()};
StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
if (Rest.startswith("NEXT:"))
return Check::CheckNext;
if (Rest.consume_front("COUNT-")) {
int64_t Count;
if (Rest.consumeInteger(10, Count))
// Error happened in parsing integer.
return {Check::CheckBadCount, Rest};
if (Count <= 0 || Count > INT32_MAX)
return {Check::CheckBadCount, Rest};
if (!Rest.consume_front(":"))
return {Check::CheckBadCount, Rest};
return {Check::FileCheckType(Check::CheckPlain).setCount(Count), Rest};
}
if (Rest.startswith("SAME:"))
return Check::CheckSame;
if (Rest.consume_front("NEXT:"))
return {Check::CheckNext, Rest};
if (Rest.startswith("NOT:"))
return Check::CheckNot;
if (Rest.consume_front("SAME:"))
return {Check::CheckSame, Rest};
if (Rest.startswith("DAG:"))
return Check::CheckDAG;
if (Rest.consume_front("NOT:"))
return {Check::CheckNot, Rest};
if (Rest.startswith("LABEL:"))
return Check::CheckLabel;
if (Rest.consume_front("DAG:"))
return {Check::CheckDAG, Rest};
if (Rest.startswith("EMPTY:"))
return Check::CheckEmpty;
if (Rest.consume_front("LABEL:"))
return {Check::CheckLabel, Rest};
if (Rest.consume_front("EMPTY:"))
return {Check::CheckEmpty, Rest};
// You can't combine -NOT with another suffix.
if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") ||
Rest.startswith("NEXT-NOT:") || Rest.startswith("NOT-NEXT:") ||
Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:") ||
Rest.startswith("EMPTY-NOT:") || Rest.startswith("NOT-EMPTY:"))
return Check::CheckBadNot;
return {Check::CheckBadNot, Rest};
return Check::CheckNone;
return {Check::CheckNone, Rest};
}
// From the given position, find the next character after the word.
@ -651,8 +645,12 @@ static size_t SkipWord(StringRef Str, size_t Loc) {
/// 2) The found prefix must be followed by a valid check type suffix using \c
/// FindCheckType above.
///
/// The first match of the regular expression to satisfy these two is returned,
/// otherwise an empty StringRef is returned to indicate failure.
/// Returns a pair of StringRefs into the Buffer, which combines:
/// - the first match of the regular expression to satisfy these two is
/// returned,
/// otherwise an empty StringRef is returned to indicate failure.
/// - buffer rewound to the location right after parsed suffix, for parsing
/// to continue from
///
/// If this routine returns a valid prefix, it will also shrink \p Buffer to
/// start at the beginning of the returned prefix, increment \p LineNumber for
@ -661,16 +659,16 @@ static size_t SkipWord(StringRef Str, size_t Loc) {
///
/// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy
/// is unspecified.
static StringRef FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
unsigned &LineNumber,
Check::FileCheckType &CheckTy) {
static std::pair<StringRef, StringRef>
FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
unsigned &LineNumber, Check::FileCheckType &CheckTy) {
SmallVector<StringRef, 2> Matches;
while (!Buffer.empty()) {
// Find the first (longest) match using the RE.
if (!PrefixRE.match(Buffer, &Matches))
// No match at all, bail.
return StringRef();
return {StringRef(), StringRef()};
StringRef Prefix = Matches[0];
Matches.clear();
@ -690,11 +688,12 @@ static StringRef FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
// intentional and unintentional uses of this feature.
if (Skipped.empty() || !IsPartOfWord(Skipped.back())) {
// Now extract the type.
CheckTy = FindCheckType(Buffer, Prefix);
StringRef AfterSuffix;
std::tie(CheckTy, AfterSuffix) = FindCheckType(Buffer, Prefix);
// If we've found a valid check type for this prefix, we're done.
if (CheckTy != Check::CheckNone)
return Prefix;
return {Prefix, AfterSuffix};
}
// If we didn't successfully find a prefix, we need to skip this invalid
@ -704,7 +703,7 @@ static StringRef FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
}
// We ran out of buffer while skipping partial matches so give up.
return StringRef();
return {StringRef(), StringRef()};
}
/// Read the check file, which specifies the sequence of expected strings.
@ -742,19 +741,26 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer,
Check::FileCheckType CheckTy;
// See if a prefix occurs in the memory buffer.
StringRef UsedPrefix = FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber,
CheckTy);
StringRef UsedPrefix;
StringRef AfterSuffix;
std::tie(UsedPrefix, AfterSuffix) =
FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber, CheckTy);
if (UsedPrefix.empty())
break;
assert(UsedPrefix.data() == Buffer.data() &&
"Failed to move Buffer's start forward, or pointed prefix outside "
"of the buffer!");
assert(AfterSuffix.data() >= Buffer.data() &&
AfterSuffix.data() < Buffer.data() + Buffer.size() &&
"Parsing after suffix doesn't start inside of buffer!");
// Location to use for error messages.
const char *UsedPrefixStart = UsedPrefix.data();
// Skip the buffer to the end.
Buffer = Buffer.drop_front(UsedPrefix.size() + CheckTypeSize(CheckTy));
// Skip the buffer to the end of parsed suffix (or just prefix, if no good
// suffix was processed).
Buffer = AfterSuffix.empty() ? Buffer.drop_front(UsedPrefix.size())
: AfterSuffix;
// Complain about useful-looking but unsupported suffixes.
if (CheckTy == Check::CheckBadNot) {
@ -763,6 +769,14 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer,
return true;
}
// Complain about invalid count specification.
if (CheckTy == Check::CheckBadCount) {
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,
"invalid count in -COUNT specification on prefix '" +
UsedPrefix + "'");
return true;
}
// Okay, we found the prefix, yay. Remember the rest of the line, but ignore
// leading whitespace.
if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
@ -845,9 +859,9 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer,
static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat,
StringRef Buffer, StringMap<StringRef> &VariableTable,
size_t MatchPos, size_t MatchLen,
const FileCheckRequest &Req) {
int MatchedCount, StringRef Buffer,
StringMap<StringRef> &VariableTable, size_t MatchPos,
size_t MatchLen, const FileCheckRequest &Req) {
if (ExpectedMatch) {
if (!Req.Verbose)
return;
@ -857,37 +871,46 @@ static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
SMLoc MatchStart = SMLoc::getFromPointer(Buffer.data() + MatchPos);
SMLoc MatchEnd = SMLoc::getFromPointer(Buffer.data() + MatchPos + MatchLen);
SMRange MatchRange(MatchStart, MatchEnd);
std::string Message = formatv("{0}: {1} string found in input",
Pat.getCheckTy().getDescription(Prefix),
(ExpectedMatch ? "expected" : "excluded"))
.str();
if (Pat.getCount() > 1)
Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
SM.PrintMessage(
Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error,
CheckTypeName(Prefix, Pat.getCheckTy()) + ": " +
(ExpectedMatch ? "expected" : "excluded") +
" string found in input");
Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
SM.PrintMessage(MatchStart, SourceMgr::DK_Note, "found here", {MatchRange});
Pat.PrintVariableUses(SM, Buffer, VariableTable, MatchRange);
}
static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
const FileCheckString &CheckStr, StringRef Buffer,
StringMap<StringRef> &VariableTable, size_t MatchPos,
size_t MatchLen, FileCheckRequest &Req) {
const FileCheckString &CheckStr, int MatchedCount,
StringRef Buffer, StringMap<StringRef> &VariableTable,
size_t MatchPos, size_t MatchLen,
FileCheckRequest &Req) {
PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
Buffer, VariableTable, MatchPos, MatchLen, Req);
MatchedCount, Buffer, VariableTable, MatchPos, MatchLen, Req);
}
static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat,
StringRef Buffer,
StringMap<StringRef> &VariableTable,
StringRef Prefix, SMLoc Loc,
const FileCheckPattern &Pat, int MatchedCount,
StringRef Buffer, StringMap<StringRef> &VariableTable,
bool VerboseVerbose) {
if (!ExpectedMatch && !VerboseVerbose)
return;
// Otherwise, we have an error, emit an error message.
SM.PrintMessage(Loc,
ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark,
CheckTypeName(Prefix, Pat.getCheckTy()) + ": " +
(ExpectedMatch ? "expected" : "excluded") +
" string not found in input");
std::string Message = formatv("{0}: {1} string not found in input",
Pat.getCheckTy().getDescription(Prefix),
(ExpectedMatch ? "expected" : "excluded"))
.str();
if (Pat.getCount() > 1)
Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
SM.PrintMessage(
Loc, ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark, Message);
// Print the "scanning from here" line. If the current position is at the
// end of a line, advance to the start of the next line.
@ -903,11 +926,11 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
}
static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
const FileCheckString &CheckStr, StringRef Buffer,
StringMap<StringRef> &VariableTable,
const FileCheckString &CheckStr, int MatchedCount,
StringRef Buffer, StringMap<StringRef> &VariableTable,
bool VerboseVerbose) {
PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
Buffer, VariableTable, VerboseVerbose);
MatchedCount, Buffer, VariableTable, VerboseVerbose);
}
/// Count the number of newlines in the specified range.
@ -953,18 +976,38 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
}
// Match itself from the last position after matching CHECK-DAG.
StringRef MatchBuffer = Buffer.substr(LastPos);
size_t MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable);
if (MatchPos == StringRef::npos) {
PrintNoMatch(true, SM, *this, MatchBuffer, VariableTable, Req.VerboseVerbose);
return StringRef::npos;
size_t LastMatchEnd = LastPos;
size_t FirstMatchPos = 0;
// Go match the pattern Count times. Majority of patterns only match with
// count 1 though.
assert(Pat.getCount() != 0 && "pattern count can not be zero");
for (int i = 1; i <= Pat.getCount(); i++) {
StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
size_t CurrentMatchLen;
// get a match at current start point
size_t MatchPos = Pat.Match(MatchBuffer, CurrentMatchLen, VariableTable);
if (i == 1)
FirstMatchPos = LastPos + MatchPos;
// report
if (MatchPos == StringRef::npos) {
PrintNoMatch(true, SM, *this, i, MatchBuffer, VariableTable,
Req.VerboseVerbose);
return StringRef::npos;
}
PrintMatch(true, SM, *this, i, MatchBuffer, VariableTable, MatchPos,
CurrentMatchLen, Req);
// move start point after the match
LastMatchEnd += MatchPos + CurrentMatchLen;
}
PrintMatch(true, SM, *this, MatchBuffer, VariableTable, MatchPos, MatchLen, Req);
// Full match len counts from first match pos.
MatchLen = LastMatchEnd - FirstMatchPos;
// Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
// or CHECK-NOT
if (!IsLabelScanMode) {
StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
StringRef SkippedRegion = Buffer.substr(LastPos, FirstMatchPos - LastPos);
// If this check is a "CHECK-NEXT", verify that the previous match was on
// the previous line (i.e. that there is one newline between them).
@ -982,7 +1025,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
return StringRef::npos;
}
return LastPos + MatchPos;
return FirstMatchPos;
}
/// Verify there is a single line in the given buffer.
@ -1072,12 +1115,12 @@ bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable);
if (Pos == StringRef::npos) {
PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, Buffer,
PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
VariableTable, Req.VerboseVerbose);
continue;
}
PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, Buffer, VariableTable,
PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, VariableTable,
Pos, MatchLen, Req);
return true;
@ -1133,15 +1176,15 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
// With a group of CHECK-DAGs, a single mismatching means the match on
// that group of CHECK-DAGs fails immediately.
if (MatchPosBuf == StringRef::npos) {
PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, MatchBuffer,
PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer,
VariableTable, Req.VerboseVerbose);
return StringRef::npos;
}
// Re-calc it as the offset relative to the start of the original string.
MatchPos += MatchPosBuf;
if (Req.VerboseVerbose)
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, Buffer, VariableTable,
MatchPos, MatchLen, Req);
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer,
VariableTable, MatchPos, MatchLen, Req);
MatchRange M{MatchPos, MatchPos + MatchLen};
if (Req.AllowDeprecatedDagOverlap) {
// We don't need to track all matches in this mode, so we just maintain
@ -1182,7 +1225,7 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
MatchPos = MI->End;
}
if (!Req.VerboseVerbose)
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, Buffer, VariableTable,
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, VariableTable,
MatchPos, MatchLen, Req);
// Handle the end of a CHECK-DAG group.

View File

@ -0,0 +1,100 @@
;
; Basic error checking.
;
this is something else
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK-ERR1 2>&1 | FileCheck %s --check-prefix=ERRCOUNT1
CHECK-ERR1-COUNT-xx: this
ERRCOUNT1: [[@LINE-1]]:18: error: invalid count in -COUNT specification on prefix 'CHECK-ERR1'
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK-ERR2 2>&1 | FileCheck %s --check-prefix=ERRCOUNT2
CHECK-ERR2-COUNT-0x1: something
ERRCOUNT2: [[@LINE-1]]:19: error: invalid count in -COUNT specification on prefix 'CHECK-ERR2'
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK-ERR3 2>&1 | FileCheck %s --check-prefix=ERRCOUNT3
CHECK-ERR3-COUNT-100x: else
ERRCOUNT3: [[@LINE-1]]:21: error: invalid count in -COUNT specification on prefix 'CHECK-ERR3'
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK-ERR4 2>&1 | FileCheck %s --check-prefix=ERRCOUNT4
CHECK-ERR4-COUNT-0: else
ERRCOUNT4: [[@LINE-1]]:19: error: invalid count in -COUNT specification on prefix 'CHECK-ERR4'
;
; Main functionality
;
this is duplicate
this is duplicate
this is not duplicate
this is duplicate
this is duplicate
this is duplicate
; RUN: FileCheck %s --input-file %s --check-prefix=CHECK-CNT1
CHECK-CNT1-COUNT-1: this is duplicate
CHECK-CNT1: this is duplicate
CHECK-CNT1-NEXT: this is not duplicate
; RUN: FileCheck %s --input-file %s --check-prefix=CHECK-CNT2
CHECK-CNT2-COUNT-2: this is duplicate
CHECK-CNT2: this is not duplicate
; RUN: FileCheck %s --input-file %s --check-prefix=CHECK-CNT3
CHECK-CNT3-COUNT-2: this is duplicate
CHECK-CNT3: this is not duplicate
CHECK-CNT3-COUNT-3: this is duplicate
CHECK-CNT3-NOT: {{^}}this is duplicate
; RUN: FileCheck %s --input-file %s --check-prefix=CHECK-CNT4
CHECK-CNT4-COUNT-5: this is duplicate
CHECK-CNT4-EMPTY:
Many-label:
-many-
-many-
-many-
-many-
-many-
-many-
-many-
-many-
-many-
-many-
-many-
-many-
-many-
-many-
-many-
-many-
; RUN: FileCheck %s --input-file %s --check-prefix=CHECK-CNTMANY
CHECK-CNTMANY-COUNT-2: this is duplicate
CHECK-CNTMANY-LABEL: Many-label:
CHECK-CNTMANY-EMPTY:
CHECK-CNTMANY-COUNT-16: {{^}}-many-
CHECK-CNTMANY-EMPTY:
;
; Tests on mismatches:
;
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK-MIS1 2>&1 | FileCheck %s --check-prefix=MISCOUNT1
CHECK-MIS1-COUNT-3: this is duplicate
CHECK-MIS1: {{^}}this is not duplicate
MISCOUNT1: [[@LINE-1]]:13: error: CHECK-MIS1: expected string not found in input
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK-MIS2 2>&1 | FileCheck %s --check-prefix=MISCOUNT2
CHECK-MIS2-COUNT-10: {{^this is duplicate}}
CHECK-MIS2: {{^}}this is not duplicate
MISCOUNT2: [[@LINE-2]]:22: error: CHECK-MIS2-COUNT: expected string not found in input
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK-MIS3 2>&1 | FileCheck %s --check-prefix=MISCOUNT3
CHECK-MIS3-COUNT-5: this is duplicate
CHECK-MIS3-EMPTY:
CHECK-MIS3-LABEL: Many-label:
CHECK-MIS3-EMPTY:
CHECK-MIS3-COUNT-160: {{^}}-many-
CHECK-MIS3-EMPTY:
MISCOUNT3: [[@LINE-2]]:23: error: CHECK-MIS3-COUNT: expected string not found in input (17 out of 160)