1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00

[FileCheck] Add CHECK-EMPTY directive for checking for blank lines

Prior to this change, there was no clean way of getting FileCheck to
check that a line is completely empty. The expected way of using
"CHECK: {{^$}}" does not work because the '^' matches the end of the
previous match (this behaviour may be desirable in certain instances).
For the same reason, "CHECK-NEXT: {{^$}}" will fail when the previous
match was at the end of the line, as the pattern will match there.
Using the recommended [[:space:]] to match an explicit new line could
also match a space, and thus is not always desired. Literal '\n'
matches also do not work. A workaround was suggested in the review, but
it is a little clunky.

This change adds a new directive that behaves the same as CHECK-NEXT,
except that it only matches against empty lines (nothing, not even
whitespace, is allowed). As with CHECK-NEXT, it will fail if more than
one newline occurs before the next blank line. Example usage:
; test.txt
foo

bar
; CHECK: foo
; CHECK-EMPTY:
; CHECK-NEXT: bar

Differential Revision: https://reviews.llvm.org/D28896

Reviewed by: probinson

llvm-svn: 335613
This commit is contained in:
James Henderson 2018-06-26 15:15:45 +00:00
parent 974db97095
commit de9948f983
3 changed files with 108 additions and 10 deletions

View File

@ -241,6 +241,25 @@ For example, the following works like you'd expect:
it and the previous directive. A "``CHECK-SAME:``" cannot be the first
directive in a file.
The "CHECK-EMPTY:" directive
~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you need to check that the next line has nothing on it, not even whitespace,
you can use the "``CHECK-EMPTY:``" directive.
.. code-block:: llvm
foo
bar
; CHECK: foo
; CHECK-EMPTY:
; CHECK-NEXT: bar
Just like "``CHECK-NEXT:``" the directive will fail if there is more than one
newline before it finds the next blank line, and it cannot be the first
directive in a file.
The "CHECK-NOT:" directive
~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,45 @@
; basic functionality
; RUN: FileCheck %s --input-file %s --check-prefix=CHECK1
foo
bar
CHECK1: foo
CHECK1-EMPTY:
CHECK1-NEXT: bar
; next line must be blank
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK2A 2>&1 | FileCheck %s --check-prefix=CHECK2B
badger
CHECK2A: badger
CHECK2A-EMPTY:
CHECK2B: CHECK2A-EMPTY: is not on the line after the previous match
; CHECK-EMPTY must have empty pattern
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK3A 2>&1 | FileCheck %s --check-prefix=CHECK3B
CHECK3A: foo
CHECK3A-EMPTY: this is not empty
CHECK3B: found non-empty check string for empty check with prefix 'CHECK3A:'
; CHECK-EMPTY cannot be the first check
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK4A 2>&1 | FileCheck %s --check-prefix=CHECK4B
CHECK4A-EMPTY:
CHECK4B: found 'CHECK4A-EMPTY' without previous 'CHECK4A: line
; CHECK-EMPTY-NOT and CHECK-NOT-EMPTY rejected
; RUN: not FileCheck %s --input-file %s --check-prefixes=CHECK5A 2>&1 | FileCheck %s --check-prefix=CHECK5C
; RUN: not FileCheck %s --input-file %s --check-prefixes=CHECK5B 2>&1 | FileCheck %s --check-prefix=CHECK5C
CHECK5A-EMPTY-NOT:
CHECK5B-NOT-EMPTY:
CHECK5C: unsupported -NOT combo on prefix 'CHECK5{{A|B}}'
; whitespace does not count as empty
; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK6A --match-full-lines 2>&1 | FileCheck %s --check-prefix=CHECK6B
CHECK6A: the next line has spaces
CHECK6A-EMPTY:
CHECK6B: expected string not found in input
; ***don't add any further blank lines after this point***
; CHECK-EMPTY, like CHECK-NEXT, will report an error if the first matching
; line is not the line immediately following the previous check.
the next line has spaces

View File

@ -97,6 +97,7 @@ enum CheckType {
CheckNot,
CheckDAG,
CheckLabel,
CheckEmpty,
/// Indicates the pattern only matches the end of file. This is used for
/// trailing CHECK-NOTs.
@ -184,12 +185,25 @@ bool Pattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
PatternStr = PatternStr.substr(0, PatternStr.size() - 1);
// Check that there is something on the line.
if (PatternStr.empty()) {
if (PatternStr.empty() && CheckTy != Check::CheckEmpty) {
SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
"found empty check string with prefix '" + Prefix + ":'");
return true;
}
if (!PatternStr.empty() && CheckTy == Check::CheckEmpty) {
SM.PrintMessage(
PatternLoc, SourceMgr::DK_Error,
"found non-empty check string for empty check with prefix '" + Prefix +
":'");
return true;
}
if (CheckTy == Check::CheckEmpty) {
RegExStr = "(\n$)";
return false;
}
// Check to see if this is a fixed string, or if it has regex pieces.
if (!MatchFullLinesHere &&
(PatternStr.size() < 2 || (PatternStr.find("{{") == StringRef::npos &&
@ -709,6 +723,9 @@ static size_t CheckTypeSize(Check::CheckType Ty) {
case Check::CheckLabel:
return sizeof("-LABEL:") - 1;
case Check::CheckEmpty:
return sizeof("-EMPTY:") - 1;
case Check::CheckEOF:
llvm_unreachable("Should not be using EOF size");
}
@ -745,10 +762,14 @@ static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) {
if (Rest.startswith("LABEL:"))
return Check::CheckLabel;
if (Rest.startswith("EMPTY:"))
return Check::CheckEmpty;
// You can't combine -NOT with another suffix.
if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") ||
Rest.startswith("NEXT-NOT:") || Rest.startswith("NOT-NEXT:") ||
Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:"))
Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:") ||
Rest.startswith("EMPTY-NOT:") || Rest.startswith("NOT-EMPTY:"))
return Check::CheckBadNot;
return Check::CheckNone;
@ -908,10 +929,13 @@ static bool ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
Buffer = Buffer.substr(EOL);
// Verify that CHECK-NEXT lines have at least one CHECK line before them.
if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame) &&
// Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them.
if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame ||
CheckTy == Check::CheckEmpty) &&
CheckStrings.empty()) {
StringRef Type = CheckTy == Check::CheckNext ? "NEXT" : "SAME";
StringRef Type = CheckTy == Check::CheckNext
? "NEXT"
: CheckTy == Check::CheckEmpty ? "EMPTY" : "SAME";
SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
SourceMgr::DK_Error,
"found '" + UsedPrefix + "-" + Type +
@ -1057,22 +1081,32 @@ size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer,
/// Verify there is a single line in the given buffer.
bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
if (Pat.getCheckTy() != Check::CheckNext)
if (Pat.getCheckTy() != Check::CheckNext &&
Pat.getCheckTy() != Check::CheckEmpty)
return false;
Twine CheckName =
Prefix +
Twine(Pat.getCheckTy() == Check::CheckEmpty ? "-EMPTY" : "-NEXT");
// Count the number of newlines between the previous match and this one.
assert(Buffer.data() !=
SM.getMemoryBuffer(SM.FindBufferContainingLoc(
SMLoc::getFromPointer(Buffer.data())))
->getBufferStart() &&
"CHECK-NEXT can't be the first check in a file");
"CHECK-NEXT and CHECK-EMPTY can't be the first check in a file");
const char *FirstNewLine = nullptr;
unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
// For CHECK-EMPTY, the preceding new line is consumed by the pattern, so
// this needs to be re-added.
if (Pat.getCheckTy() == Check::CheckEmpty)
++NumNewLines;
if (NumNewLines == 0) {
SM.PrintMessage(Loc, SourceMgr::DK_Error,
Prefix + "-NEXT: is on the same line as previous match");
CheckName + ": is on the same line as previous match");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
"'next' match was here");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
@ -1082,8 +1116,8 @@ bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
if (NumNewLines != 1) {
SM.PrintMessage(Loc, SourceMgr::DK_Error,
Prefix +
"-NEXT: is not on the line after the previous match");
CheckName +
": is not on the line after the previous match");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
"'next' match was here");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,