mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
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. llvm-svn: 194343
This commit is contained in:
parent
4fcb58f7fb
commit
59f495c8a7
@ -30,11 +30,13 @@ OPTIONS
|
|||||||
|
|
||||||
.. option:: --check-prefix prefix
|
.. option:: --check-prefix prefix
|
||||||
|
|
||||||
FileCheck searches the contents of ``match-filename`` for patterns to match.
|
FileCheck searches the contents of ``match-filename`` for patterns to
|
||||||
By default, these patterns are prefixed with "``CHECK:``". If you'd like to
|
match. By default, these patterns are prefixed with "``CHECK:``".
|
||||||
use a different prefix (e.g. because the same input file is checking multiple
|
If you'd like to use a different prefix (e.g. because the same input
|
||||||
different tool or options), the :option:`--check-prefix` argument allows you
|
file is checking multiple different tool or options), the
|
||||||
to specify a specific prefix to match.
|
: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
|
.. option:: --input-file filename
|
||||||
|
|
||||||
|
5
test/FileCheck/check-a-b-has-b.txt
Normal file
5
test/FileCheck/check-a-b-has-b.txt
Normal file
@ -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}}
|
5
test/FileCheck/check-b-a-has-b.txt
Normal file
5
test/FileCheck/check-b-a-has-b.txt
Normal file
@ -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}}
|
7
test/FileCheck/check-dag-multi-prefix-2.txt
Normal file
7
test/FileCheck/check-dag-multi-prefix-2.txt
Normal file
@ -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
|
27
test/FileCheck/check-dag-multi-prefix.txt
Normal file
27
test/FileCheck/check-dag-multi-prefix.txt
Normal file
@ -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]]
|
||||||
|
|
7
test/FileCheck/check-dag-substring-prefix.txt
Normal file
7
test/FileCheck/check-dag-substring-prefix.txt
Normal file
@ -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}}
|
6
test/FileCheck/check-multi-prefix-label.txt
Normal file
6
test/FileCheck/check-multi-prefix-label.txt
Normal file
@ -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
|
10
test/FileCheck/check-multiple-prefixes-mixed.txt
Normal file
10
test/FileCheck/check-multiple-prefixes-mixed.txt
Normal file
@ -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
|
11
test/FileCheck/check-substring-multi-prefix-2.txt
Normal file
11
test/FileCheck/check-substring-multi-prefix-2.txt
Normal file
@ -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}}
|
9
test/FileCheck/check-substring-multi-prefix.txt
Normal file
9
test/FileCheck/check-substring-multi-prefix.txt
Normal file
@ -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
|
2
test/FileCheck/first-character-match.txt
Normal file
2
test/FileCheck/first-character-match.txt
Normal file
@ -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.
|
9
test/FileCheck/multiple-missing-prefixes.txt
Normal file
9
test/FileCheck/multiple-missing-prefixes.txt
Normal file
@ -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{{:}}'
|
7
test/FileCheck/separate-multi-prefix.txt
Normal file
7
test/FileCheck/separate-multi-prefix.txt
Normal file
@ -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
|
||||||
|
|
@ -1,7 +1,9 @@
|
|||||||
// RUN: not FileCheck -check-prefix=A! -input-file %s %s 2>&1 | FileCheck -check-prefix=BAD_PREFIX %s
|
// 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: 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
|
foobar
|
||||||
; A1a-B_c: 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
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "llvm/ADT/SmallString.h"
|
#include "llvm/ADT/SmallString.h"
|
||||||
#include "llvm/ADT/StringExtras.h"
|
#include "llvm/ADT/StringExtras.h"
|
||||||
#include "llvm/ADT/StringMap.h"
|
#include "llvm/ADT/StringMap.h"
|
||||||
|
#include "llvm/ADT/StringSet.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/Support/MemoryBuffer.h"
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
#include "llvm/Support/PrettyStackTrace.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)"),
|
InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
|
||||||
cl::init("-"), cl::value_desc("filename"));
|
cl::init("-"), cl::value_desc("filename"));
|
||||||
|
|
||||||
static cl::opt<std::string>
|
static cl::list<std::string>
|
||||||
CheckPrefix("check-prefix", cl::init("CHECK"),
|
CheckPrefixes("check-prefix",
|
||||||
cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
|
cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
|
||||||
|
|
||||||
static cl::opt<bool>
|
static cl::opt<bool>
|
||||||
NoCanonicalizeWhiteSpace("strict-whitespace",
|
NoCanonicalizeWhiteSpace("strict-whitespace",
|
||||||
cl::desc("Do not treat all horizontal whitespace as equivalent"));
|
cl::desc("Do not treat all horizontal whitespace as equivalent"));
|
||||||
|
|
||||||
|
typedef cl::list<std::string>::const_iterator prefix_iterator;
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Pattern Handling Code.
|
// Pattern Handling Code.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
@ -103,11 +106,15 @@ public:
|
|||||||
/// getLoc - Return the location in source code.
|
/// getLoc - Return the location in source code.
|
||||||
SMLoc getLoc() const { return PatternLoc; }
|
SMLoc getLoc() const { return PatternLoc; }
|
||||||
|
|
||||||
/// ParsePattern - Parse the given string into the Pattern. SM provides the
|
/// ParsePattern - Parse the given string into the Pattern. Prefix provides
|
||||||
/// SourceMgr used for error reports, and LineNumber is the line number in
|
/// which prefix is being matched, SM provides the SourceMgr used for error
|
||||||
/// the input file from which the pattern string was read.
|
/// reports, and LineNumber is the line number in the input file from which
|
||||||
/// Returns true in case of an error, false otherwise.
|
/// the pattern string was read. Returns true in case of an error, false
|
||||||
bool ParsePattern(StringRef PatternStr, SourceMgr &SM, unsigned LineNumber);
|
/// otherwise.
|
||||||
|
bool ParsePattern(StringRef PatternStr,
|
||||||
|
StringRef Prefix,
|
||||||
|
SourceMgr &SM,
|
||||||
|
unsigned LineNumber);
|
||||||
|
|
||||||
/// Match - Match the pattern string against the input buffer Buffer. This
|
/// 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
|
/// 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) {
|
unsigned LineNumber) {
|
||||||
this->LineNumber = LineNumber;
|
this->LineNumber = LineNumber;
|
||||||
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
|
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
|
||||||
@ -166,7 +175,7 @@ bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM,
|
|||||||
if (PatternStr.empty()) {
|
if (PatternStr.empty()) {
|
||||||
SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
|
SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
|
||||||
"found empty check string with prefix '" +
|
"found empty check string with prefix '" +
|
||||||
CheckPrefix+":'");
|
Prefix + ":'");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,6 +603,9 @@ struct CheckString {
|
|||||||
/// Pat - The pattern to match.
|
/// Pat - The pattern to match.
|
||||||
Pattern Pat;
|
Pattern Pat;
|
||||||
|
|
||||||
|
/// Prefix - Which prefix name this check matched.
|
||||||
|
StringRef Prefix;
|
||||||
|
|
||||||
/// Loc - The location in the match file that the check string was specified.
|
/// Loc - The location in the match file that the check string was specified.
|
||||||
SMLoc Loc;
|
SMLoc Loc;
|
||||||
|
|
||||||
@ -606,8 +618,12 @@ struct CheckString {
|
|||||||
/// file).
|
/// file).
|
||||||
std::vector<Pattern> DagNotStrings;
|
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".
|
/// Check - Match check string and its "not strings" and/or "dag strings".
|
||||||
size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode,
|
size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode,
|
||||||
@ -670,43 +686,155 @@ static bool IsPartOfWord(char c) {
|
|||||||
return (isalnum(c) || c == '-' || 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()];
|
char NextChar = Buffer[Prefix.size()];
|
||||||
|
|
||||||
// Verify that the : is present after the prefix.
|
// Verify that the : is present after the prefix.
|
||||||
if (NextChar == ':') {
|
if (NextChar == ':')
|
||||||
Buffer = Buffer.substr(Prefix.size() + 1);
|
|
||||||
return Check::CheckPlain;
|
return Check::CheckPlain;
|
||||||
}
|
|
||||||
|
|
||||||
if (NextChar != '-') {
|
if (NextChar != '-')
|
||||||
Buffer = Buffer.drop_front(1);
|
|
||||||
return Check::CheckNone;
|
return Check::CheckNone;
|
||||||
}
|
|
||||||
|
|
||||||
StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
|
StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
|
||||||
if (Rest.startswith("NEXT:")) {
|
if (Rest.startswith("NEXT:"))
|
||||||
Buffer = Rest.drop_front(sizeof("NEXT:") - 1);
|
|
||||||
return Check::CheckNext;
|
return Check::CheckNext;
|
||||||
}
|
|
||||||
|
|
||||||
if (Rest.startswith("NOT:")) {
|
if (Rest.startswith("NOT:"))
|
||||||
Buffer = Rest.drop_front(sizeof("NOT:") - 1);
|
|
||||||
return Check::CheckNot;
|
return Check::CheckNot;
|
||||||
}
|
|
||||||
|
|
||||||
if (Rest.startswith("DAG:")) {
|
if (Rest.startswith("DAG:"))
|
||||||
Buffer = Rest.drop_front(sizeof("DAG:") - 1);
|
|
||||||
return Check::CheckDAG;
|
return Check::CheckDAG;
|
||||||
}
|
|
||||||
|
|
||||||
if (Rest.startswith("LABEL:")) {
|
if (Rest.startswith("LABEL:"))
|
||||||
Buffer = Rest.drop_front(sizeof("LABEL:") - 1);
|
|
||||||
return Check::CheckLabel;
|
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);
|
if (FirstPrefix.empty()) {
|
||||||
return Check::CheckNone;
|
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
|
/// ReadCheckFile - Read the check file, which specifies the sequence of
|
||||||
@ -738,32 +866,24 @@ static bool ReadCheckFile(SourceMgr &SM,
|
|||||||
unsigned LineNumber = 1;
|
unsigned LineNumber = 1;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
// See if Prefix occurs in the memory buffer.
|
Check::CheckType CheckTy;
|
||||||
size_t PrefixLoc = Buffer.find(CheckPrefix);
|
size_t PrefixLoc;
|
||||||
// If we didn't find a match, we're done.
|
|
||||||
if (PrefixLoc == StringRef::npos)
|
// See if a prefix occurs in the memory buffer.
|
||||||
|
StringRef UsedPrefix = FindFirstMatchingPrefix(Buffer,
|
||||||
|
LineNumber,
|
||||||
|
CheckTy,
|
||||||
|
PrefixLoc);
|
||||||
|
if (UsedPrefix.empty())
|
||||||
break;
|
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
|
// Location to use for error messages.
|
||||||
// found our prefix, and account for cases when PrefixLoc is 0.
|
const char *UsedPrefixStart = Buffer.data() + (PrefixLoc == 0 ? 0 : 1);
|
||||||
Buffer = Buffer.substr(std::min(PrefixLoc-1, PrefixLoc));
|
|
||||||
|
|
||||||
const char *CheckPrefixStart = Buffer.data() + (PrefixLoc == 0 ? 0 : 1);
|
// PrefixLoc is to the start of the prefix. Skip to the end.
|
||||||
|
Buffer = Buffer.drop_front(UsedPrefix.size() + CheckTypeSize(CheckTy));
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Okay, we found the prefix, yay. Remember the rest of the line, but ignore
|
// Okay, we found the prefix, yay. Remember the rest of the line, but ignore
|
||||||
// leading and trailing whitespace.
|
// leading and trailing whitespace.
|
||||||
@ -777,15 +897,15 @@ static bool ReadCheckFile(SourceMgr &SM,
|
|||||||
|
|
||||||
// Parse the pattern.
|
// Parse the pattern.
|
||||||
Pattern P(CheckTy);
|
Pattern P(CheckTy);
|
||||||
if (P.ParsePattern(Buffer.substr(0, EOL), SM, LineNumber))
|
if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Verify that CHECK-LABEL lines do not define or use variables
|
// Verify that CHECK-LABEL lines do not define or use variables
|
||||||
if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {
|
if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart),
|
SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
|
||||||
SourceMgr::DK_Error,
|
SourceMgr::DK_Error,
|
||||||
"found '"+CheckPrefix+"-LABEL:' with variable definition"
|
"found '" + UsedPrefix + "-LABEL:'"
|
||||||
" or use");
|
" with variable definition or use");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -793,10 +913,10 @@ static bool ReadCheckFile(SourceMgr &SM,
|
|||||||
|
|
||||||
// Verify that CHECK-NEXT lines have at least one CHECK line before them.
|
// Verify that CHECK-NEXT lines have at least one CHECK line before them.
|
||||||
if ((CheckTy == Check::CheckNext) && CheckStrings.empty()) {
|
if ((CheckTy == Check::CheckNext) && CheckStrings.empty()) {
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart),
|
SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
|
||||||
SourceMgr::DK_Error,
|
SourceMgr::DK_Error,
|
||||||
"found '"+CheckPrefix+"-NEXT:' without previous '"+
|
"found '" + UsedPrefix + "-NEXT:' without previous '"
|
||||||
CheckPrefix+ ": line");
|
+ UsedPrefix + ": line");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -808,22 +928,33 @@ static bool ReadCheckFile(SourceMgr &SM,
|
|||||||
|
|
||||||
// Okay, add the string we captured to the output vector and move on.
|
// Okay, add the string we captured to the output vector and move on.
|
||||||
CheckStrings.push_back(CheckString(P,
|
CheckStrings.push_back(CheckString(P,
|
||||||
|
UsedPrefix,
|
||||||
PatternLoc,
|
PatternLoc,
|
||||||
CheckTy));
|
CheckTy));
|
||||||
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
|
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()) {
|
if (!DagNotMatches.empty()) {
|
||||||
CheckStrings.push_back(CheckString(Pattern(Check::CheckEOF),
|
CheckStrings.push_back(CheckString(Pattern(Check::CheckEOF),
|
||||||
|
CheckPrefixes[0],
|
||||||
SMLoc::getFromPointer(Buffer.data()),
|
SMLoc::getFromPointer(Buffer.data()),
|
||||||
Check::CheckEOF));
|
Check::CheckEOF));
|
||||||
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
|
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CheckStrings.empty()) {
|
if (CheckStrings.empty()) {
|
||||||
errs() << "error: no check strings found with prefix '" << CheckPrefix
|
errs() << "error: no check strings found with prefix"
|
||||||
<< ":'\n";
|
<< (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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -933,7 +1064,7 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
|
|||||||
unsigned NumNewLines = CountNumNewlinesBetween(Buffer);
|
unsigned NumNewLines = CountNumNewlinesBetween(Buffer);
|
||||||
|
|
||||||
if (NumNewLines == 0) {
|
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");
|
"-NEXT: is on the same line as previous match");
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()),
|
SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()),
|
||||||
SourceMgr::DK_Note, "'next' match was here");
|
SourceMgr::DK_Note, "'next' match was here");
|
||||||
@ -943,7 +1074,7 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (NumNewLines != 1) {
|
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");
|
"-NEXT: is not on the line after the previous match");
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()),
|
SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()),
|
||||||
SourceMgr::DK_Note, "'next' match was here");
|
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),
|
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()+Pos),
|
||||||
SourceMgr::DK_Error,
|
SourceMgr::DK_Error,
|
||||||
CheckPrefix+"-NOT: string occurred!");
|
Prefix + "-NOT: string occurred!");
|
||||||
SM.PrintMessage(Pat->getLoc(), SourceMgr::DK_Note,
|
SM.PrintMessage(Pat->getLoc(), SourceMgr::DK_Note,
|
||||||
CheckPrefix+"-NOT: pattern specified here");
|
Prefix + "-NOT: pattern specified here");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1022,17 +1153,17 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
|
|||||||
// Reordered?
|
// Reordered?
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + MatchPos),
|
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + MatchPos),
|
||||||
SourceMgr::DK_Error,
|
SourceMgr::DK_Error,
|
||||||
CheckPrefix+"-DAG: found a match of CHECK-DAG"
|
Prefix + "-DAG: found a match of CHECK-DAG"
|
||||||
" reordering across a CHECK-NOT");
|
" reordering across a CHECK-NOT");
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + LastPos),
|
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + LastPos),
|
||||||
SourceMgr::DK_Note,
|
SourceMgr::DK_Note,
|
||||||
CheckPrefix+"-DAG: the farthest match of CHECK-DAG"
|
Prefix + "-DAG: the farthest match of CHECK-DAG"
|
||||||
" is found here");
|
" is found here");
|
||||||
SM.PrintMessage(NotStrings[0]->getLoc(), SourceMgr::DK_Note,
|
SM.PrintMessage(NotStrings[0]->getLoc(), SourceMgr::DK_Note,
|
||||||
CheckPrefix+"-NOT: the crossed pattern specified"
|
Prefix + "-NOT: the crossed pattern specified"
|
||||||
" here");
|
" here");
|
||||||
SM.PrintMessage(Pat.getLoc(), SourceMgr::DK_Note,
|
SM.PrintMessage(Pat.getLoc(), SourceMgr::DK_Note,
|
||||||
CheckPrefix+"-DAG: the reordered pattern specified"
|
Prefix + "-DAG: the reordered pattern specified"
|
||||||
" here");
|
" here");
|
||||||
return StringRef::npos;
|
return StringRef::npos;
|
||||||
}
|
}
|
||||||
@ -1056,10 +1187,34 @@ size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
|
|||||||
return LastPos;
|
return LastPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidateCheckPrefix() {
|
// A check prefix must contain only alphanumeric, hyphens and underscores.
|
||||||
// The check prefix must contain only alphanumeric, hyphens and underscores.
|
static bool ValidateCheckPrefix(StringRef CheckPrefix) {
|
||||||
Regex prefixValidator("^[a-zA-Z0-9_-]*$");
|
Regex Validator("^[a-zA-Z0-9_-]*$");
|
||||||
return prefixValidator.match(CheckPrefix);
|
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) {
|
int main(int argc, char **argv) {
|
||||||
@ -1067,13 +1222,15 @@ int main(int argc, char **argv) {
|
|||||||
PrettyStackTraceProgram X(argc, argv);
|
PrettyStackTraceProgram X(argc, argv);
|
||||||
cl::ParseCommandLineOptions(argc, argv);
|
cl::ParseCommandLineOptions(argc, argv);
|
||||||
|
|
||||||
if (!ValidateCheckPrefix()) {
|
if (!ValidateCheckPrefixes()) {
|
||||||
errs() << "Supplied check-prefix is invalid! Prefixes must start with a "
|
errs() << "Supplied check-prefix is invalid! Prefixes must be unique and "
|
||||||
"letter and contain only alphanumeric characters, hyphens and "
|
"start with a letter and contain only alphanumeric characters, "
|
||||||
"underscores\n";
|
"hyphens and underscores\n";
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddCheckPrefixIfNeeded();
|
||||||
|
|
||||||
SourceMgr SM;
|
SourceMgr SM;
|
||||||
|
|
||||||
// Read the expected strings from the check file.
|
// Read the expected strings from the check file.
|
||||||
|
Loading…
Reference in New Issue
Block a user