From ee4f5eae1c416eddbecbb2d742e2bb8dc0032bf6 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Sun, 10 Nov 2013 02:04:09 +0000 Subject: 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 --- utils/FileCheck/FileCheck.cpp | 317 +++++++++++++++++++++++++++++++----------- 1 file changed, 237 insertions(+), 80 deletions(-) (limited to 'utils') diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index 77721f9..c9eb8a6 100644 --- a/utils/FileCheck/FileCheck.cpp +++ b/utils/FileCheck/FileCheck.cpp @@ -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 InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), cl::init("-"), cl::value_desc("filename")); -static cl::opt -CheckPrefix("check-prefix", cl::init("CHECK"), - cl::desc("Prefix to use from check file (defaults to 'CHECK')")); +static cl::list +CheckPrefixes("check-prefix", + cl::desc("Prefix to use from check file (defaults to 'CHECK')")); static cl::opt NoCanonicalizeWhiteSpace("strict-whitespace", cl::desc("Do not treat all horizontal whitespace as equivalent")); +typedef cl::list::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 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,45 +686,157 @@ 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; - } - Buffer = Buffer.drop_front(1); 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; + } + + 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 /// expected strings. The strings are added to the CheckStrings vector. /// Returns true in case of an error, false otherwise. @@ -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. -- cgit v1.1