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:
parent
184774cd2c
commit
82854f2e28
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
100
test/FileCheck/check-count.txt
Normal file
100
test/FileCheck/check-count.txt
Normal 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)
|
Loading…
Reference in New Issue
Block a user