summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/CommandGuide/FileCheck.rst49
-rw-r--r--test/FileCheck/check-label.txt51
-rw-r--r--utils/FileCheck/FileCheck.cpp123
3 files changed, 194 insertions, 29 deletions
diff --git a/docs/CommandGuide/FileCheck.rst b/docs/CommandGuide/FileCheck.rst
index 809eee0469..9d7f63cea9 100644
--- a/docs/CommandGuide/FileCheck.rst
+++ b/docs/CommandGuide/FileCheck.rst
@@ -243,6 +243,55 @@ occurrences matching ``CHECK-DAG:`` after ``CHECK-NOT:``. For example,
This case will reject input strings where ``BEFORE`` occurs after ``AFTER``.
+The "CHECK-LABEL:" directive
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes in a file containing multiple tests divided into logical blocks, one
+or more ``CHECK:`` directives may inadvertently succeed by matching lines in a
+later block. While an error will usually eventually be generated, the check
+flagged as causing the error may not actually bear any relationship to the
+actual source of the problem.
+
+In order to produce better error messages in these cases, the "``CHECK-LABEL:``"
+directive can be used. It is treated identically to a normal ``CHECK``
+directive except that the FileCheck utility makes an additional assumption that
+a line matched by the directive cannot also be matched by any other check
+present in ``match-filename``; this is intended to be used for lines containing
+labels or other unique identifiers. Conceptually, the presence of
+``CHECK-LABEL`` divides the input stream into separate blocks, each of which is
+processed independently, preventing a ``CHECK:`` directive in one block
+matching a line in another block. For example,
+
+.. code-block:: llvm
+
+ define %struct.C* @C_ctor_base(%struct.C* %this, i32 %x) {
+ entry:
+ ; CHECK-LABEL: C_ctor_base:
+ ; CHECK: mov [[SAVETHIS:r[0-9]+]], r0
+ ; CHECK: bl A_ctor_base
+ ; CHECK: mov r0, [[SAVETHIS]]
+ %0 = bitcast %struct.C* %this to %struct.A*
+ %call = tail call %struct.A* @A_ctor_base(%struct.A* %0)
+ %1 = bitcast %struct.C* %this to %struct.B*
+ %call2 = tail call %struct.B* @B_ctor_base(%struct.B* %1, i32 %x)
+ ret %struct.C* %this
+ }
+
+ define %struct.D* @D_ctor_base(%struct.D* %this, i32 %x) {
+ entry:
+ ; CHECK-LABEL: D_ctor_base:
+
+The use of ``CHECK-LABEL:`` directives in this case ensures that the three
+``CHECK:`` directives only accept lines corresponding to the body of the
+``@C_ctor_base`` function, even if the patterns match lines found later in
+the file.
+
+There is no requirement that ``CHECK-LABEL:`` directives contain strings that
+correspond to actual syntactic labels in a source or output language: they must
+simply uniquely match a single line in the file being verified.
+
+``CHECK-LABEL:`` directives cannot contain variable definitions or uses.
+
FileCheck Pattern Matching Syntax
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/test/FileCheck/check-label.txt b/test/FileCheck/check-label.txt
new file mode 100644
index 0000000000..27f0beeb29
--- /dev/null
+++ b/test/FileCheck/check-label.txt
@@ -0,0 +1,51 @@
+; RUN: FileCheck -input-file %s %s -check-prefix=CHECKOK
+; RUN: not FileCheck -input-file %s %s -check-prefix=CHECKFAIL 2>&1 | FileCheck %s -check-prefix=CHECKERROR
+
+label0:
+a
+b
+
+label1:
+b
+c
+
+label2:
+a
+c
+
+; CHECKOK-LABEL: {{^}}label0:
+; CHECKOK: {{^}}a
+; CHECKOK: {{^}}b
+
+; CHECKOK-LABEL: {{^}}label1:
+; CHECKOK: {{^}}b
+; CHECKOK: {{^}}c
+
+; CHECKOK-LABEL: {{^}}label2:
+; CHECKOK: {{^}}a
+; CHECKOK: {{^}}c
+
+; CHECKFAIL-LABEL: {{^}}label0:
+; CHECKFAIL: {{^}}a
+; CHECKFAIL: {{^}}b
+; CHECKFAIL: {{^}}c
+
+; CHECKERROR: expected string not found in input
+; CHECKERROR-NEXT: CHECKFAIL: {{[{][{]\^[}][}]}}c
+
+; CHECKFAIL-LABEL: {{^}}label1:
+; CHECKFAIL: {{^}}a
+; CHECKFAIL: {{^}}b
+; CHECKFAIL: {{^}}c
+
+; CHECKERROR: expected string not found in input
+; CHECKERROR-NEXT: CHECKFAIL: {{[{][{]\^[}][}]}}a
+
+; CHECKFAIL-LABEL: {{^}}label2:
+; CHECKFAIL: {{^}}a
+; CHECKFAIL: {{^}}b
+; CHECKFAIL: {{^}}c
+
+; CHECKERROR: expected string not found in input
+; CHECKERROR-NEXT: CHECKFAIL: {{[{][{]\^[}][}]}}b
+
diff --git a/utils/FileCheck/FileCheck.cpp b/utils/FileCheck/FileCheck.cpp
index 5e2d93bf39..e252db9141 100644
--- a/utils/FileCheck/FileCheck.cpp
+++ b/utils/FileCheck/FileCheck.cpp
@@ -115,6 +115,9 @@ public:
void PrintFailureInfo(const SourceMgr &SM, StringRef Buffer,
const StringMap<StringRef> &VariableTable) const;
+ bool hasVariable() const { return !(VariableUses.empty() &&
+ VariableDefs.empty()); }
+
void setMatchNot(bool Not) { MatchNot = Not; }
bool getMatchNot() const { return MatchNot; }
@@ -594,17 +597,21 @@ struct CheckString {
/// to a CHECK: directive.
bool IsCheckNext;
+ /// IsCheckLabel - This is true if this is a CHECK-LABEL: directive (as
+ /// opposed to a CHECK: directive.
+ bool IsCheckLabel;
+
/// DagNotStrings - These are all of the strings that are disallowed from
/// occurring between this match string and the previous one (or start of
/// file).
std::vector<Pattern> DagNotStrings;
- CheckString(const Pattern &P, SMLoc L, bool isCheckNext)
- : Pat(P), Loc(L), IsCheckNext(isCheckNext) {}
+ CheckString(const Pattern &P, SMLoc L, bool isCheckNext, bool isCheckLabel)
+ : Pat(P), Loc(L), IsCheckNext(isCheckNext), IsCheckLabel(isCheckLabel) {}
/// Check - Match check string and its "not strings" and/or "dag strings".
- size_t Check(const SourceMgr &SM, StringRef Buffer, size_t &MatchLen,
- StringMap<StringRef> &VariableTable) const;
+ size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabel,
+ size_t &MatchLen, StringMap<StringRef> &VariableTable) const;
/// CheckNext - Verify there is a single line in the given buffer.
bool CheckNext(const SourceMgr &SM, StringRef Buffer) const;
@@ -703,7 +710,8 @@ static bool ReadCheckFile(SourceMgr &SM,
// When we find a check prefix, keep track of whether we find CHECK: or
// CHECK-NEXT:
- bool IsCheckNext = false, IsCheckNot = false, IsCheckDag = false;
+ bool IsCheckNext = false, IsCheckNot = false, IsCheckDag = false,
+ IsCheckLabel = false;
// Verify that the : is present after the prefix.
if (Buffer[CheckPrefix.size()] == ':') {
@@ -720,6 +728,10 @@ static bool ReadCheckFile(SourceMgr &SM,
memcmp(Buffer.data()+CheckPrefix.size(), "-DAG:", 5) == 0) {
Buffer = Buffer.substr(CheckPrefix.size()+5);
IsCheckDag = true;
+ } else if (Buffer.size() > CheckPrefix.size()+7 &&
+ memcmp(Buffer.data()+CheckPrefix.size(), "-LABEL:", 7) == 0) {
+ Buffer = Buffer.substr(CheckPrefix.size()+7);
+ IsCheckLabel = true;
} else {
Buffer = Buffer.substr(1);
continue;
@@ -740,6 +752,15 @@ static bool ReadCheckFile(SourceMgr &SM,
if (P.ParsePattern(Buffer.substr(0, EOL), SM, LineNumber))
return true;
+ // Verify that CHECK-LABEL lines do not define or use variables
+ if (IsCheckLabel && P.hasVariable()) {
+ SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart),
+ SourceMgr::DK_Error,
+ "found '"+CheckPrefix+"-LABEL:' with variable definition"
+ " or use'");
+ return true;
+ }
+
P.setMatchNot(IsCheckNot);
P.setMatchDag(IsCheckDag);
@@ -763,7 +784,8 @@ static bool ReadCheckFile(SourceMgr &SM,
// Okay, add the string we captured to the output vector and move on.
CheckStrings.push_back(CheckString(P,
PatternLoc,
- IsCheckNext));
+ IsCheckNext,
+ IsCheckLabel));
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
}
@@ -771,6 +793,7 @@ static bool ReadCheckFile(SourceMgr &SM,
if (!DagNotMatches.empty()) {
CheckStrings.push_back(CheckString(Pattern(true),
SMLoc::getFromPointer(Buffer.data()),
+ false,
false));
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
}
@@ -829,15 +852,17 @@ static unsigned CountNumNewlinesBetween(StringRef Range) {
}
size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer,
- size_t &MatchLen,
+ bool IsLabel, size_t &MatchLen,
StringMap<StringRef> &VariableTable) const {
size_t LastPos = 0;
std::vector<const Pattern *> NotStrings;
- // Match "dag strings" (with mixed "not strings" if any).
- LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable);
- if (LastPos == StringRef::npos)
- return StringRef::npos;
+ if (!IsLabel) {
+ // Match "dag strings" (with mixed "not strings" if any).
+ LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable);
+ if (LastPos == StringRef::npos)
+ return StringRef::npos;
+ }
// Match itself from the last position after matching CHECK-DAG.
StringRef MatchBuffer = Buffer.substr(LastPos);
@@ -848,17 +873,19 @@ size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer,
}
MatchPos += LastPos;
- StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
+ if (!IsLabel) {
+ StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
- // 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).
- if (CheckNext(SM, SkippedRegion))
- return StringRef::npos;
+ // 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).
+ if (CheckNext(SM, SkippedRegion))
+ return StringRef::npos;
- // If this match had "not strings", verify that they don't exist in the
- // skipped region.
- if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable))
- return StringRef::npos;
+ // If this match had "not strings", verify that they don't exist in the
+ // skipped region.
+ if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable))
+ return StringRef::npos;
+ }
return MatchPos;
}
@@ -1040,18 +1067,56 @@ int main(int argc, char **argv) {
// file.
StringRef Buffer = F->getBuffer();
- for (unsigned StrNo = 0, e = CheckStrings.size(); StrNo != e; ++StrNo) {
- const CheckString &CheckStr = CheckStrings[StrNo];
+ bool hasError = false;
- // Find StrNo in the file.
- size_t MatchLen = 0;
- size_t MatchPos = CheckStr.Check(SM, Buffer, MatchLen, VariableTable);
+ unsigned i = 0, j = 0, e = CheckStrings.size();
+
+ while (true) {
+ StringRef CheckRegion;
+ if (j == e) {
+ CheckRegion = Buffer;
+ } else {
+ const CheckString &CheckLabelStr = CheckStrings[j];
+ if (!CheckLabelStr.IsCheckLabel) {
+ ++j;
+ continue;
+ }
+
+ // Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG
+ size_t MatchLabelLen = 0;
+ size_t MatchLabelPos = CheckLabelStr.Check(SM, Buffer, true,
+ MatchLabelLen, VariableTable);
+ if (MatchLabelPos == StringRef::npos) {
+ hasError = true;
+ break;
+ }
- if (MatchPos == StringRef::npos)
- return 1;
+ CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen);
+ Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen);
+ ++j;
+ }
- Buffer = Buffer.substr(MatchPos + MatchLen);
+ for ( ; i != j; ++i) {
+ const CheckString &CheckStr = CheckStrings[i];
+
+ // Check each string within the scanned region, including a second check
+ // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)
+ size_t MatchLen = 0;
+ size_t MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen,
+ VariableTable);
+
+ if (MatchPos == StringRef::npos) {
+ hasError = true;
+ i = j;
+ break;
+ }
+
+ CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
+ }
+
+ if (j == e)
+ break;
}
- return 0;
+ return hasError ? 1 : 0;
}