``CHECK-LABEL:`` directives cannot contain variable definitions or uses.
+Directive modifiers
+~~~~~~~~~~~~~~~~~~~
+
+A directive modifier can be append to a directive by following the directive
+with ``{<modifier>}`` where the only supported value for ``<modifier>`` is
+``LITERAL``.
+
+The ``LITERAL`` directive modifier can be used to perform a literal match. The
+modifier results in the directive not recognizing any syntax to perform regex
+matching, variable capture or any substitutions. This is useful when the text
+to match would require excessive escaping otherwise. For example, the
+following will perform literal matches rather than considering these as
+regular expressions:
+
+.. code-block:: text
+
+ Input: [[[10, 20]], [[30, 40]]]
+ Output %r10: [[10, 20]]
+ Output %r10: [[30, 40]]
+
+ ; CHECK{LITERAL}: [[[10, 20]], [[30, 40]]]
+ ; CHECK-DAG{LITERAL}: [[30, 40]]
+ ; CHECK-DAG{LITERAL}: [[10, 20]]
+
FileCheck Regex Matching Syntax
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/SourceMgr.h"
+#include <bitset>
#include <string>
#include <vector>
CheckBadCount
};
+enum FileCheckKindModifier {
+ /// Modifies directive to perform literal match.
+ ModifierLiteral = 0,
+
+ // The number of modifier.
+ Size
+};
+
class FileCheckType {
FileCheckKind Kind;
int Count; ///< optional Count for some checks
+ /// Modifers for the check directive.
+ std::bitset<FileCheckKindModifier::Size> Modifiers;
public:
- FileCheckType(FileCheckKind Kind = CheckNone) : Kind(Kind), Count(1) {}
+ FileCheckType(FileCheckKind Kind = CheckNone)
+ : Kind(Kind), Count(1), Modifiers() {}
FileCheckType(const FileCheckType &) = default;
FileCheckType &operator=(const FileCheckType &) = default;
int getCount() const { return Count; }
FileCheckType &setCount(int C);
+ bool isLiteralMatch() const {
+ return Modifiers[FileCheckKindModifier::ModifierLiteral];
+ }
+ FileCheckType &setLiteralMatch(bool Literal = true) {
+ Modifiers.set(FileCheckKindModifier::ModifierLiteral, Literal);
+ return *this;
+ }
+
// \returns a description of \p Prefix.
std::string getDescription(StringRef Prefix) const;
+
+ // \returns a description of \p Modifiers.
+ std::string getModifiersDescription() const;
};
} // namespace Check
return false;
}
+ // If literal check, set fixed string.
+ if (CheckTy.isLiteralMatch()) {
+ FixedStr = PatternStr;
+ 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 &&
return *this;
}
+std::string Check::FileCheckType::getModifiersDescription() const {
+ if (Modifiers.none())
+ return "";
+ std::string Ret;
+ raw_string_ostream OS(Ret);
+ OS << '{';
+ if (isLiteralMatch())
+ OS << "LITERAL";
+ OS << '}';
+ return OS.str();
+}
+
std::string Check::FileCheckType::getDescription(StringRef Prefix) const {
+ // Append directive modifiers.
+ auto WithModifiers = [this, Prefix](StringRef Str) -> std::string {
+ return (Prefix + Str + getModifiersDescription()).str();
+ };
+
switch (Kind) {
case Check::CheckNone:
return "invalid";
case Check::CheckPlain:
if (Count > 1)
- return Prefix.str() + "-COUNT";
- return std::string(Prefix);
+ return WithModifiers("-COUNT");
+ return WithModifiers("");
case Check::CheckNext:
- return Prefix.str() + "-NEXT";
+ return WithModifiers("-NEXT");
case Check::CheckSame:
- return Prefix.str() + "-SAME";
+ return WithModifiers("-SAME");
case Check::CheckNot:
- return Prefix.str() + "-NOT";
+ return WithModifiers("-NOT");
case Check::CheckDAG:
- return Prefix.str() + "-DAG";
+ return WithModifiers("-DAG");
case Check::CheckLabel:
- return Prefix.str() + "-LABEL";
+ return WithModifiers("-LABEL");
case Check::CheckEmpty:
- return Prefix.str() + "-EMPTY";
+ return WithModifiers("-EMPTY");
case Check::CheckComment:
return std::string(Prefix);
case Check::CheckEOF:
if (Buffer.size() <= Prefix.size())
return {Check::CheckNone, StringRef()};
- char NextChar = Buffer[Prefix.size()];
-
- StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
-
+ StringRef Rest = Buffer.drop_front(Prefix.size());
// Check for comment.
if (llvm::is_contained(Req.CommentPrefixes, Prefix)) {
- if (NextChar == ':')
+ if (Rest.consume_front(":"))
return {Check::CheckComment, Rest};
// Ignore a comment prefix if it has a suffix like "-NOT".
return {Check::CheckNone, StringRef()};
}
- // Verify that the : is present after the prefix.
- if (NextChar == ':')
+ auto ConsumeModifiers = [&](Check::FileCheckType Ret)
+ -> std::pair<Check::FileCheckType, StringRef> {
+ if (Rest.consume_front(":"))
+ return {Ret, Rest};
+ if (!Rest.consume_front("{"))
+ return {Check::CheckNone, StringRef()};
+
+ // Parse the modifiers, speparated by commas.
+ do {
+ // Allow whitespace in modifiers list.
+ Rest = Rest.ltrim();
+ if (Rest.consume_front("LITERAL"))
+ Ret.setLiteralMatch();
+ else
+ return {Check::CheckNone, Rest};
+ // Allow whitespace in modifiers list.
+ Rest = Rest.ltrim();
+ } while (Rest.consume_front(","));
+ if (!Rest.consume_front("}:"))
+ return {Check::CheckNone, Rest};
+ return {Ret, Rest};
+ };
+
+ // Verify that the prefix is followed by directive modifiers or a colon.
+ if (Rest.consume_front(":"))
return {Check::CheckPlain, Rest};
+ if (Rest.front() == '{')
+ return ConsumeModifiers(Check::CheckPlain);
- if (NextChar != '-')
+ if (!Rest.consume_front("-"))
return {Check::CheckNone, StringRef()};
if (Rest.consume_front("COUNT-")) {
return {Check::CheckBadCount, Rest};
if (Count <= 0 || Count > INT32_MAX)
return {Check::CheckBadCount, Rest};
- if (!Rest.consume_front(":"))
+ if (Rest.front() != ':' && Rest.front() != '{')
return {Check::CheckBadCount, Rest};
- return {Check::FileCheckType(Check::CheckPlain).setCount(Count), Rest};
+ return ConsumeModifiers(
+ Check::FileCheckType(Check::CheckPlain).setCount(Count));
}
- if (Rest.consume_front("NEXT:"))
- return {Check::CheckNext, Rest};
-
- if (Rest.consume_front("SAME:"))
- return {Check::CheckSame, Rest};
-
- if (Rest.consume_front("NOT:"))
- return {Check::CheckNot, Rest};
-
- if (Rest.consume_front("DAG:"))
- return {Check::CheckDAG, Rest};
-
- if (Rest.consume_front("LABEL:"))
- return {Check::CheckLabel, Rest};
-
- if (Rest.consume_front("EMPTY:"))
- return {Check::CheckEmpty, Rest};
-
// 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("EMPTY-NOT:") || Rest.startswith("NOT-EMPTY:"))
return {Check::CheckBadNot, Rest};
+ if (Rest.consume_front("NEXT"))
+ return ConsumeModifiers(Check::CheckNext);
+
+ if (Rest.consume_front("SAME"))
+ return ConsumeModifiers(Check::CheckSame);
+
+ if (Rest.consume_front("NOT"))
+ return ConsumeModifiers(Check::CheckNot);
+
+ if (Rest.consume_front("DAG"))
+ return ConsumeModifiers(Check::CheckDAG);
+
+ if (Rest.consume_front("LABEL"))
+ return ConsumeModifiers(Check::CheckLabel);
+
+ if (Rest.consume_front("EMPTY"))
+ return ConsumeModifiers(Check::CheckEmpty);
+
return {Check::CheckNone, Rest};
}
--- /dev/null
+; RUN: FileCheck -check-prefix=A -input-file %s %s
+
+;; This tests the LITERAL directive modifier.
+
+The result is "5371, 5372, 5373, 5374"
+
+The result is "[[[5371]], [[5372]], [[5373]], [[5374]]]"
+[[[5375]], [[5376]],
+[[[5377]], [[5378]],
+{{there you go.*}}
+
+[[10]]
+[[20]]
+[[50]]
+
+;; These should all not match.
+; A{}: 5371, 5372,
+; A{LITERAL} 5371, 5372,
+; A{LITERAL 5371, 5372,
+; A{LITERAL,} 5371, 5372,
+; A{, LITERAL} 5371, 5372,
+
+; A: 5371, 5372,
+; A-SAME: 5373, 5374
+; A{LITERAL}: [[[5371]], [[5372]],
+; A-SAME{LITERAL}: [[5373]], [[5374]]]
+
+;; Modifier list allows whitespace.
+; A{ LITERAL }: [[[5375]], [[5376]],
+;; Modifiers are combined into a set and repetition is allowed.
+; A{LITERAL , LITERAL}: [[[5377]], [[5378]],
+
+; A-NEXT{LITERAL}: {{there you go.*}}
+; A-NOT{LITERAL}: [[50]]
+; A-DAG{LITERAL}: [[20]]
+; A-DAG{LITERAL}: [[10]]
+; A{LITERAL}: [[50]]
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck %s --input-file %s --check-prefix=INVALID 2>&1 | \
+; RUN: FileCheck %s --check-prefix=CHECK-INVALID
+
+;; Ensure invalid modifier skipped.
+
+; INVALID{BADMODIFIER}: 6371, 6372,
+; CHECK-INVALID: no check strings found with prefix 'INVALID
+
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck %s --input-file %s --check-prefix=CHECK-ERRNOT 2>&1 | \
+; RUN: FileCheck %s --check-prefix=ERRNOT
+
+;; This ensures a failure is correctly reported when a NOT directive with a
+;; LITERAL modifier matches.
+
+[[a]]
+[[b]]
+[[c]]
+
+; CHECK-ERRNOT{LITERAL}: [[a]]
+; CHECK-ERRNOT-NOT{LITERAL}: [[b]]
+; CHECK-ERRNOT{LITERAL}: [[c]]
+; ERRNOT: no match expected