summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/docs/CommandGuide/FileCheck.rst23
-rw-r--r--llvm/include/llvm/Support/FileCheck.h26
-rw-r--r--llvm/lib/Support/FileCheck.cpp249
-rw-r--r--llvm/test/FileCheck/check-count.txt100
4 files changed, 293 insertions, 105 deletions
diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst
index 6581b33ba1c..89831cafe48 100644
--- a/llvm/docs/CommandGuide/FileCheck.rst
+++ b/llvm/docs/CommandGuide/FileCheck.rst
@@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h
index de8d46bcb8f..2ff690d4d17 100644
--- a/llvm/include/llvm/Support/FileCheck.h
+++ b/llvm/include/llvm/Support/FileCheck.h
@@ -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);
diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp
index 386fa80b8b9..ca09c5df5e9 100644
--- a/llvm/lib/Support/FileCheck.cpp
+++ b/llvm/lib/Support/FileCheck.cpp
@@ -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()};
+
+ 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};
+ }
- StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
- if (Rest.startswith("NEXT:"))
- return Check::CheckNext;
+ if (Rest.consume_front("NEXT:"))
+ return {Check::CheckNext, Rest};
- if (Rest.startswith("SAME:"))
- return Check::CheckSame;
+ if (Rest.consume_front("SAME:"))
+ return {Check::CheckSame, Rest};
- if (Rest.startswith("NOT:"))
- return Check::CheckNot;
+ if (Rest.consume_front("NOT:"))
+ return {Check::CheckNot, Rest};
- if (Rest.startswith("DAG:"))
- return Check::CheckDAG;
+ if (Rest.consume_front("DAG:"))
+ return {Check::CheckDAG, Rest};
- if (Rest.startswith("LABEL:"))
- return Check::CheckLabel;
+ if (Rest.consume_front("LABEL:"))
+ return {Check::CheckLabel, Rest};
- if (Rest.startswith("EMPTY:"))
- return Check::CheckEmpty;
+ 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.
diff --git a/llvm/test/FileCheck/check-count.txt b/llvm/test/FileCheck/check-count.txt
new file mode 100644
index 00000000000..1782728949a
--- /dev/null
+++ b/llvm/test/FileCheck/check-count.txt
@@ -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)
OpenPOWER on IntegriCloud