diff --git a/docs/CommandGuide/FileCheck.rst b/docs/CommandGuide/FileCheck.rst index a71007b6161..5a60d60ae3d 100644 --- a/docs/CommandGuide/FileCheck.rst +++ b/docs/CommandGuide/FileCheck.rst @@ -30,11 +30,13 @@ OPTIONS .. option:: --check-prefix prefix - FileCheck searches the contents of ``match-filename`` for patterns to match. - By default, these patterns are prefixed with "``CHECK:``". If you'd like to - use a different prefix (e.g. because the same input file is checking multiple - different tool or options), the :option:`--check-prefix` argument allows you - to specify a specific prefix to match. + FileCheck searches the contents of ``match-filename`` for patterns to + match. By default, these patterns are prefixed with "``CHECK:``". + If you'd like to use a different prefix (e.g. because the same input + file is checking multiple different tool or options), the + :option:`--check-prefix` argument allows you to specify one or more + prefixes to match. Multiple prefixes are useful for tests which might + change for different run options, but most lines remain the same. .. option:: --input-file filename diff --git a/test/FileCheck/check-a-b-has-b.txt b/test/FileCheck/check-a-b-has-b.txt new file mode 100644 index 00000000000..4d64d098ae2 --- /dev/null +++ b/test/FileCheck/check-a-b-has-b.txt @@ -0,0 +1,5 @@ +; RUN: FileCheck -check-prefix=A -check-prefix=B -input-file %s %s + +this is the string to be matched + +; B-DAG: this is the string to be {{matched}} diff --git a/test/FileCheck/check-b-a-has-b.txt b/test/FileCheck/check-b-a-has-b.txt new file mode 100644 index 00000000000..ac149906c52 --- /dev/null +++ b/test/FileCheck/check-b-a-has-b.txt @@ -0,0 +1,5 @@ +; RUN: FileCheck -check-prefix=B -check-prefix=A -input-file %s %s + +this is the string to be matched + +; B-DAG: this is the string to be {{matched}} diff --git a/test/FileCheck/check-dag-multi-prefix-2.txt b/test/FileCheck/check-dag-multi-prefix-2.txt new file mode 100644 index 00000000000..4add70da160 --- /dev/null +++ b/test/FileCheck/check-dag-multi-prefix-2.txt @@ -0,0 +1,7 @@ +; RUN: FileCheck -check-prefix=A -input-file %s %s + +this should be matched + +; B-DAG: foo + +; A-DAG: {{this}} should be matched diff --git a/test/FileCheck/check-dag-multi-prefix.txt b/test/FileCheck/check-dag-multi-prefix.txt new file mode 100644 index 00000000000..95dfe5a5192 --- /dev/null +++ b/test/FileCheck/check-dag-multi-prefix.txt @@ -0,0 +1,27 @@ +; RUN: FileCheck -check-prefix=A -check-prefix=B -input-file %s %s + +add r10, r1, r2 +add r11, r3, r4 +mul r5, r10, r11 + +mul r11, r3, r4 +mul r10, r1, r2 +add r5, r10, r11 + +add r11, r3, r4 +add r10, r1, r2 +mul r5, r10, r11 + +; B-DAG: add [[REG1:r[0-9]+]], r1, r2 +; B-DAG: add [[REG2:r[0-9]+]], r3, r4 +; B: mul r5, [[REG1]], [[REG2]] + +; A-DAG: mul [[REG1:r[0-9]+]], r1, r2 +; A-DAG: mul [[REG2:r[0-9]+]], r3, r4 +; A: add r5, [[REG1]], [[REG2]] + +; B-DAG: add [[REG1:r[0-9]+]], r1, r2 +; B-DAG: add [[REG2:r[0-9]+]], r3, r4 +; B-NOT: xor +; B-DAG: mul r5, [[REG1]], [[REG2]] + diff --git a/test/FileCheck/check-dag-substring-prefix.txt b/test/FileCheck/check-dag-substring-prefix.txt new file mode 100644 index 00000000000..49d4b2b9ba9 --- /dev/null +++ b/test/FileCheck/check-dag-substring-prefix.txt @@ -0,0 +1,7 @@ +; RUN: not FileCheck -check-prefix=A -check-prefix=AA -input-file %s %s + +this is the string to be matched +this should also be matched + +; BAA-DAG: this is the string to be {{matched}} +; BAA-DAG: this should also be {{matched}} diff --git a/test/FileCheck/check-multi-prefix-label.txt b/test/FileCheck/check-multi-prefix-label.txt new file mode 100644 index 00000000000..41fe64151d4 --- /dev/null +++ b/test/FileCheck/check-multi-prefix-label.txt @@ -0,0 +1,6 @@ +// RUN: FileCheck -check-prefix=ONE -check-prefix=TWO -input-file %s %s + +foo +bar +; ONE-LABEL: {{f}}oo +; TWO-NEXT: {{b}}ar diff --git a/test/FileCheck/check-multiple-prefixes-mixed.txt b/test/FileCheck/check-multiple-prefixes-mixed.txt new file mode 100644 index 00000000000..cd3b70a425e --- /dev/null +++ b/test/FileCheck/check-multiple-prefixes-mixed.txt @@ -0,0 +1,10 @@ +// RUN: FileCheck -check-prefix=B -check-prefix=BOTH -input-file %s %s +// RUN: FileCheck -check-prefix=A -check-prefix=BOTH -input-file %s %s + +; A: {{a}}aaaaa +; B: {{b}}bbbb +; BOTH: {{q}}qqqqq +aaaaaa +bbbbb +qqqqqq +ccccc diff --git a/test/FileCheck/check-substring-multi-prefix-2.txt b/test/FileCheck/check-substring-multi-prefix-2.txt new file mode 100644 index 00000000000..618a2884d4a --- /dev/null +++ b/test/FileCheck/check-substring-multi-prefix-2.txt @@ -0,0 +1,11 @@ +; RUN: FileCheck -check-prefix=FOO -check-prefix=FOOBAR -check-prefix=BARFOO -input-file %s %s +; RUN: FileCheck -check-prefix=FOOBAR -check-prefix=FOO -check-prefix=BARFOO -input-file %s %s +; RUN: FileCheck -check-prefix=FOOBAR -check-prefix=BARFOO -check-prefix=FOO -input-file %s %s + +this is the match +this is another + +FOO +FOOBAR +FOOBAR: this is the {{match}} +BARFOO: this is {{another}} diff --git a/test/FileCheck/check-substring-multi-prefix.txt b/test/FileCheck/check-substring-multi-prefix.txt new file mode 100644 index 00000000000..b7edb8b530e --- /dev/null +++ b/test/FileCheck/check-substring-multi-prefix.txt @@ -0,0 +1,9 @@ +// RUN: FileCheck -check-prefix=AAAOVERLAP -check-prefix=OVERLAP -input-file %s %s + +foo +bar +buzz + +OVERLAP: foo +AAAOVERLAP: bar +OVERLAP: buzz diff --git a/test/FileCheck/first-character-match.txt b/test/FileCheck/first-character-match.txt new file mode 100644 index 00000000000..4b09c21df58 --- /dev/null +++ b/test/FileCheck/first-character-match.txt @@ -0,0 +1,2 @@ +RUN: FileCheck -check-prefix=RUN -input-file %s %s +// Prefix is at the first character in the file. The run line then matches itself. diff --git a/test/FileCheck/multiple-missing-prefixes.txt b/test/FileCheck/multiple-missing-prefixes.txt new file mode 100644 index 00000000000..cb557d9f260 --- /dev/null +++ b/test/FileCheck/multiple-missing-prefixes.txt @@ -0,0 +1,9 @@ +// RUN: FileCheck -check-prefix=ANOTHER-PREFIX -input-file %s %s +// RUN: not FileCheck -check-prefix=PREFIX1 -check-prefix=PREFIX2 -input-file %s %s 2>&1 | FileCheck -strict-whitespace -check-prefix=CHECK-NONEXISTENT-PREFIX -check-prefix=ALSO-NONEXISTENT %s + +foobar +; ANOTHER-PREFIX: foobar + +; We use regex to match the colon so that FileCheck won't think it is a check +; prefix. +; CHECK-NONEXISTENT-PREFIX: error: no check strings found with prefixes 'PREFIX1{{:}}', 'PREFIX2{{:}}' diff --git a/test/FileCheck/separate-multi-prefix.txt b/test/FileCheck/separate-multi-prefix.txt new file mode 100644 index 00000000000..5578d7f1b96 --- /dev/null +++ b/test/FileCheck/separate-multi-prefix.txt @@ -0,0 +1,7 @@ +// RUN: not FileCheck -check-prefix=SOMEPREFIX -input-file %s %s +// RUN: FileCheck -check-prefix=ANOTHER -input-file %s %s + +asdf +; SOMEPREFIX: {{t}}his_is_not_asdf +; ANOTHER: {{a}}sdf + diff --git a/test/FileCheck/validate-check-prefix.txt b/test/FileCheck/validate-check-prefix.txt index fe219a319cd..db3392d5819 100644 --- a/test/FileCheck/validate-check-prefix.txt +++ b/test/FileCheck/validate-check-prefix.txt @@ -1,7 +1,9 @@ // RUN: not FileCheck -check-prefix=A! -input-file %s %s 2>&1 | FileCheck -check-prefix=BAD_PREFIX %s // RUN: FileCheck -check-prefix=A1a-B_c -input-file %s %s - +// RUN: not FileCheck -check-prefix=REPEAT -check-prefix=REPEAT -input-file %s %s 2>&1 | FileCheck -check-prefix=BAD_PREFIX %s +// RUN: not FileCheck -check-prefix=VALID -check-prefix=A! -input-file %s %s 2>&1 | FileCheck -check-prefix=BAD_PREFIX %s foobar ; A1a-B_c: foobar -; BAD_PREFIX: Supplied check-prefix is invalid! Prefixes must start with a letter and contain only alphanumeric characters, hyphens and underscores +; BAD_PREFIX: Supplied check-prefix is invalid! Prefixes must be + unique and start with a letter and contain only alphanumeric characters, hyphens and underscores diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp index 77721f94515..c9eb8a650c8 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,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.