Allow multiple check prefixes in FileCheck.

This is useful if you want to run multiple variations
of a single test, and the majority of check lines
should be the same.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@194343 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Matt Arsenault
2013-11-10 02:04:09 +00:00
parent 432bdf6571
commit ee4f5eae1c
15 changed files with 353 additions and 87 deletions

View File

@ -20,6 +20,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
@ -42,14 +43,16 @@ static cl::opt<std::string>
InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
cl::init("-"), cl::value_desc("filename"));
static cl::opt<std::string>
CheckPrefix("check-prefix", cl::init("CHECK"),
cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
static cl::list<std::string>
CheckPrefixes("check-prefix",
cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
static cl::opt<bool>
NoCanonicalizeWhiteSpace("strict-whitespace",
cl::desc("Do not treat all horizontal whitespace as equivalent"));
typedef cl::list<std::string>::const_iterator prefix_iterator;
//===----------------------------------------------------------------------===//
// Pattern Handling Code.
//===----------------------------------------------------------------------===//
@ -103,11 +106,15 @@ public:
/// getLoc - Return the location in source code.
SMLoc getLoc() const { return PatternLoc; }
/// ParsePattern - Parse the given string into the Pattern. SM provides the
/// SourceMgr used for error reports, and LineNumber is the line number in
/// the input file from which the pattern string was read.
/// Returns true in case of an error, false otherwise.
bool ParsePattern(StringRef PatternStr, SourceMgr &SM, unsigned LineNumber);
/// ParsePattern - Parse the given string into the Pattern. Prefix provides
/// which prefix is being matched, SM provides the SourceMgr used for error
/// reports, and LineNumber is the line number in the input file from which
/// the pattern string was read. Returns true in case of an error, false
/// otherwise.
bool ParsePattern(StringRef PatternStr,
StringRef Prefix,
SourceMgr &SM,
unsigned LineNumber);
/// Match - Match the pattern string against the input buffer Buffer. This
/// returns the position that is matched or npos if there is no match. If
@ -152,7 +159,9 @@ private:
};
bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM,
bool Pattern::ParsePattern(StringRef PatternStr,
StringRef Prefix,
SourceMgr &SM,
unsigned LineNumber) {
this->LineNumber = LineNumber;
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
@ -166,7 +175,7 @@ bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM,
if (PatternStr.empty()) {
SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
"found empty check string with prefix '" +
CheckPrefix+":'");
Prefix + ":'");
return true;
}
@ -594,6 +603,9 @@ struct CheckString {
/// Pat - The pattern to match.
Pattern Pat;
/// Prefix - Which prefix name this check matched.
StringRef Prefix;
/// Loc - The location in the match file that the check string was specified.
SMLoc Loc;
@ -606,8 +618,12 @@ struct CheckString {
/// file).
std::vector<Pattern> DagNotStrings;
CheckString(const Pattern &P, SMLoc L, Check::CheckType Ty)
: Pat(P), Loc(L), CheckTy(Ty) {}
CheckString(const Pattern &P,
StringRef S,
SMLoc L,
Check::CheckType Ty)
: Pat(P), Prefix(S), Loc(L), CheckTy(Ty) {}
/// Check - Match check string and its "not strings" and/or "dag strings".
size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode,
@ -670,43 +686,155 @@ static bool IsPartOfWord(char c) {
return (isalnum(c) || c == '-' || c == '_');
}
static Check::CheckType FindCheckType(StringRef &Buffer, StringRef Prefix) {
// Get the size of the prefix extension.
static size_t CheckTypeSize(Check::CheckType Ty) {
switch (Ty) {
case Check::CheckNone:
return 0;
case Check::CheckPlain:
return sizeof(":") - 1;
case Check::CheckNext:
return sizeof("-NEXT:") - 1;
case Check::CheckNot:
return sizeof("-NOT:") - 1;
case Check::CheckDAG:
return sizeof("-DAG:") - 1;
case Check::CheckLabel:
return sizeof("-LABEL:") - 1;
case Check::CheckEOF:
llvm_unreachable("Should not be using EOF size");
}
llvm_unreachable("Bad check type");
}
static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) {
char NextChar = Buffer[Prefix.size()];
// Verify that the : is present after the prefix.
if (NextChar == ':') {
Buffer = Buffer.substr(Prefix.size() + 1);
if (NextChar == ':')
return Check::CheckPlain;
}
if (NextChar != '-') {
Buffer = Buffer.drop_front(1);
if (NextChar != '-')
return Check::CheckNone;
}
StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
if (Rest.startswith("NEXT:")) {
Buffer = Rest.drop_front(sizeof("NEXT:") - 1);
if (Rest.startswith("NEXT:"))
return Check::CheckNext;
}
if (Rest.startswith("NOT:")) {
Buffer = Rest.drop_front(sizeof("NOT:") - 1);
if (Rest.startswith("NOT:"))
return Check::CheckNot;
}
if (Rest.startswith("DAG:")) {
Buffer = Rest.drop_front(sizeof("DAG:") - 1);
if (Rest.startswith("DAG:"))
return Check::CheckDAG;
}
if (Rest.startswith("LABEL:")) {
Buffer = Rest.drop_front(sizeof("LABEL:") - 1);
if (Rest.startswith("LABEL:"))
return Check::CheckLabel;
return Check::CheckNone;
}
// From the given position, find the next character after the word.
static size_t SkipWord(StringRef Str, size_t Loc) {
while (Loc < Str.size() && IsPartOfWord(Str[Loc]))
++Loc;
return Loc;
}
// Try to find the first match in buffer for any prefix. If a valid match is
// found, return that prefix and set its type and location. If there are almost
// matches (e.g. the actual prefix string is found, but is not an actual check
// string), but no valid match, return an empty string and set the position to
// resume searching from. If no partial matches are found, return an empty
// string and the location will be StringRef::npos. If one prefix is a substring
// of another, the maximal match should be found. e.g. if "A" and "AA" are
// prefixes then AA-CHECK: should match the second one.
static StringRef FindFirstCandidateMatch(StringRef &Buffer,
Check::CheckType &CheckTy,
size_t &CheckLoc) {
StringRef FirstPrefix;
size_t FirstLoc = StringRef::npos;
size_t SearchLoc = StringRef::npos;
Check::CheckType FirstTy = Check::CheckNone;
CheckTy = Check::CheckNone;
CheckLoc = StringRef::npos;
for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end();
I != E; ++I) {
StringRef Prefix(*I);
size_t PrefixLoc = Buffer.find(Prefix);
if (PrefixLoc == StringRef::npos)
continue;
// Track where we are searching for invalid prefixes that look almost right.
// We need to only advance to the first partial match on the next attempt
// since a partial match could be a substring of a later, valid prefix.
// Need to skip to the end of the word, otherwise we could end up
// matching a prefix in a substring later.
if (PrefixLoc < SearchLoc)
SearchLoc = SkipWord(Buffer, PrefixLoc);
// We only want to find the first match to avoid skipping some.
if (PrefixLoc > FirstLoc)
continue;
StringRef Rest = Buffer.drop_front(PrefixLoc);
// Make sure we have actually found the prefix, and not a word containing
// it. This should also prevent matching the wrong prefix when one is a
// substring of another.
if (PrefixLoc != 0 && IsPartOfWord(Buffer[PrefixLoc - 1]))
continue;
Check::CheckType Ty = FindCheckType(Rest, Prefix);
if (Ty == Check::CheckNone)
continue;
FirstLoc = PrefixLoc;
FirstTy = Ty;
FirstPrefix = Prefix;
}
Buffer = Buffer.drop_front(1);
return Check::CheckNone;
if (FirstPrefix.empty()) {
CheckLoc = SearchLoc;
} else {
CheckTy = FirstTy;
CheckLoc = FirstLoc;
}
return FirstPrefix;
}
static StringRef FindFirstMatchingPrefix(StringRef &Buffer,
unsigned &LineNumber,
Check::CheckType &CheckTy,
size_t &CheckLoc) {
while (!Buffer.empty()) {
StringRef Prefix = FindFirstCandidateMatch(Buffer, CheckTy, CheckLoc);
// If we found a real match, we are done.
if (!Prefix.empty()) {
LineNumber += Buffer.substr(0, CheckLoc).count('\n');
return Prefix;
}
// We didn't find any almost matches either, we are also done.
if (CheckLoc == StringRef::npos)
return StringRef();
LineNumber += Buffer.substr(0, CheckLoc + 1).count('\n');
// Advance to the last possible match we found and try again.
Buffer = Buffer.drop_front(CheckLoc + 1);
}
return StringRef();
}
/// ReadCheckFile - Read the check file, which specifies the sequence of
@ -738,32 +866,24 @@ static bool ReadCheckFile(SourceMgr &SM,
unsigned LineNumber = 1;
while (1) {
// See if Prefix occurs in the memory buffer.
size_t PrefixLoc = Buffer.find(CheckPrefix);
// If we didn't find a match, we're done.
if (PrefixLoc == StringRef::npos)
Check::CheckType CheckTy;
size_t PrefixLoc;
// See if a prefix occurs in the memory buffer.
StringRef UsedPrefix = FindFirstMatchingPrefix(Buffer,
LineNumber,
CheckTy,
PrefixLoc);
if (UsedPrefix.empty())
break;
LineNumber += Buffer.substr(0, PrefixLoc).count('\n');
Buffer = Buffer.drop_front(PrefixLoc);
// Keep the charcter before our prefix so we can validate that we have
// found our prefix, and account for cases when PrefixLoc is 0.
Buffer = Buffer.substr(std::min(PrefixLoc-1, PrefixLoc));
// Location to use for error messages.
const char *UsedPrefixStart = Buffer.data() + (PrefixLoc == 0 ? 0 : 1);
const char *CheckPrefixStart = Buffer.data() + (PrefixLoc == 0 ? 0 : 1);
// Make sure we have actually found our prefix, and not a word containing
// our prefix.
if (PrefixLoc != 0 && IsPartOfWord(Buffer[0])) {
Buffer = Buffer.substr(CheckPrefix.size());
continue;
}
// When we find a check prefix, keep track of what kind of type of CHECK we
// have.
Check::CheckType CheckTy = FindCheckType(Buffer, CheckPrefix);
if (CheckTy == Check::CheckNone)
continue;
// PrefixLoc is to the start of the prefix. Skip to the end.
Buffer = Buffer.drop_front(UsedPrefix.size() + CheckTypeSize(CheckTy));
// Okay, we found the prefix, yay. Remember the rest of the line, but ignore
// leading and trailing whitespace.
@ -777,15 +897,15 @@ static bool ReadCheckFile(SourceMgr &SM,
// Parse the pattern.
Pattern P(CheckTy);
if (P.ParsePattern(Buffer.substr(0, EOL), SM, LineNumber))
if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber))
return true;
// Verify that CHECK-LABEL lines do not define or use variables
if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {
SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart),
SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
SourceMgr::DK_Error,
"found '"+CheckPrefix+"-LABEL:' with variable definition"
" or use");
"found '" + UsedPrefix + "-LABEL:'"
" with variable definition or use");
return true;
}
@ -793,10 +913,10 @@ static bool ReadCheckFile(SourceMgr &SM,
// Verify that CHECK-NEXT lines have at least one CHECK line before them.
if ((CheckTy == Check::CheckNext) && CheckStrings.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart),
SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
SourceMgr::DK_Error,
"found '"+CheckPrefix+"-NEXT:' without previous '"+
CheckPrefix+ ": line");
"found '" + UsedPrefix + "-NEXT:' without previous '"
+ UsedPrefix + ": line");
return true;
}
@ -808,22 +928,33 @@ static bool ReadCheckFile(SourceMgr &SM,
// Okay, add the string we captured to the output vector and move on.
CheckStrings.push_back(CheckString(P,
UsedPrefix,
PatternLoc,
CheckTy));
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
}
// Add an EOF pattern for any trailing CHECK-DAG/-NOTs.
// Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first
// prefix as a filler for the error message.
if (!DagNotMatches.empty()) {
CheckStrings.push_back(CheckString(Pattern(Check::CheckEOF),
CheckPrefixes[0],
SMLoc::getFromPointer(Buffer.data()),
Check::CheckEOF));
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
}
if (CheckStrings.empty()) {
errs() << "error: no check strings found with prefix '" << CheckPrefix
<< ":'\n";
errs() << "error: no check strings found with prefix"
<< (CheckPrefixes.size() > 1 ? "es " : " ");
for (size_t I = 0, N = CheckPrefixes.size(); I != N; ++I) {
StringRef Prefix(CheckPrefixes[I]);
errs() << '\'' << Prefix << ":'";
if (I != N - 1)
errs() << ", ";
}
errs() << '\n';
return true;
}
@ -933,7 +1064,7 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
unsigned NumNewLines = CountNumNewlinesBetween(Buffer);
if (NumNewLines == 0) {
SM.PrintMessage(Loc, SourceMgr::DK_Error, CheckPrefix+
SM.PrintMessage(Loc, SourceMgr::DK_Error, Prefix +
"-NEXT: is on the same line as previous match");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()),
SourceMgr::DK_Note, "'next' match was here");
@ -943,7 +1074,7 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
}
if (NumNewLines != 1) {
SM.PrintMessage(Loc, SourceMgr::DK_Error, CheckPrefix+
SM.PrintMessage(Loc, SourceMgr::DK_Error, Prefix +
"-NEXT: is not on the line after the previous match");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()),
SourceMgr::DK_Note, "'next' match was here");
@ -970,9 +1101,9 @@ bool CheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()+Pos),
SourceMgr::DK_Error,
CheckPrefix+"-NOT: string occurred!");
Prefix + "-NOT: string occurred!");
SM.PrintMessage(Pat->getLoc(), SourceMgr::DK_Note,
CheckPrefix+"-NOT: pattern specified here");
Prefix + "-NOT: pattern specified here");
return true;
}
@ -1022,17 +1153,17 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
// Reordered?
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + MatchPos),
SourceMgr::DK_Error,
CheckPrefix+"-DAG: found a match of CHECK-DAG"
Prefix + "-DAG: found a match of CHECK-DAG"
" reordering across a CHECK-NOT");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + LastPos),
SourceMgr::DK_Note,
CheckPrefix+"-DAG: the farthest match of CHECK-DAG"
Prefix + "-DAG: the farthest match of CHECK-DAG"
" is found here");
SM.PrintMessage(NotStrings[0]->getLoc(), SourceMgr::DK_Note,
CheckPrefix+"-NOT: the crossed pattern specified"
Prefix + "-NOT: the crossed pattern specified"
" here");
SM.PrintMessage(Pat.getLoc(), SourceMgr::DK_Note,
CheckPrefix+"-DAG: the reordered pattern specified"
Prefix + "-DAG: the reordered pattern specified"
" here");
return StringRef::npos;
}
@ -1056,10 +1187,34 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
return LastPos;
}
bool ValidateCheckPrefix() {
// The check prefix must contain only alphanumeric, hyphens and underscores.
Regex prefixValidator("^[a-zA-Z0-9_-]*$");
return prefixValidator.match(CheckPrefix);
// A check prefix must contain only alphanumeric, hyphens and underscores.
static bool ValidateCheckPrefix(StringRef CheckPrefix) {
Regex Validator("^[a-zA-Z0-9_-]*$");
return Validator.match(CheckPrefix);
}
static bool ValidateCheckPrefixes() {
StringSet<> PrefixSet;
for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end();
I != E; ++I) {
StringRef Prefix(*I);
if (!PrefixSet.insert(Prefix))
return false;
if (!ValidateCheckPrefix(Prefix))
return false;
}
return true;
}
// I don't think there's a way to specify an initial value for cl::list,
// so if nothing was specified, add the default
static void AddCheckPrefixIfNeeded() {
if (CheckPrefixes.empty())
CheckPrefixes.push_back("CHECK");
}
int main(int argc, char **argv) {
@ -1067,13 +1222,15 @@ int main(int argc, char **argv) {
PrettyStackTraceProgram X(argc, argv);
cl::ParseCommandLineOptions(argc, argv);
if (!ValidateCheckPrefix()) {
errs() << "Supplied check-prefix is invalid! Prefixes must start with a "
"letter and contain only alphanumeric characters, hyphens and "
"underscores\n";
if (!ValidateCheckPrefixes()) {
errs() << "Supplied check-prefix is invalid! Prefixes must be unique and "
"start with a letter and contain only alphanumeric characters, "
"hyphens and underscores\n";
return 2;
}
AddCheckPrefixIfNeeded();
SourceMgr SM;
// Read the expected strings from the check file.