From 5507f6688def557aca3dbbf7cf53cdf6a13ad8f8 Mon Sep 17 00:00:00 2001 From: James Henderson Date: Tue, 26 Jun 2018 15:15:45 +0000 Subject: [PATCH] [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 --- llvm/docs/CommandGuide/FileCheck.rst | 19 ++++++++++++ llvm/test/FileCheck/check-empty-tag.txt | 45 +++++++++++++++++++++++++++ llvm/utils/FileCheck/FileCheck.cpp | 54 +++++++++++++++++++++++++++------ 3 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 llvm/test/FileCheck/check-empty-tag.txt diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst index 9078f65..e169ea5 100644 --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -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 ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/llvm/test/FileCheck/check-empty-tag.txt b/llvm/test/FileCheck/check-empty-tag.txt new file mode 100644 index 0000000..70d3afcd --- /dev/null +++ b/llvm/test/FileCheck/check-empty-tag.txt @@ -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 + \ No newline at end of file diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp index 64aa0f2..7415734 100644 --- a/llvm/utils/FileCheck/FileCheck.cpp +++ b/llvm/utils/FileCheck/FileCheck.cpp @@ -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, -- 2.7.4