diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -61,7 +61,10 @@ CheckBadNot, /// Marks when parsing found a -COUNT directive with invalid count value. - CheckBadCount + CheckBadCount, + + /// Marks when parsing found a directive followed by whitespace (not a colon). + CheckBadColon, }; class FileCheckType { diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -1088,6 +1088,8 @@ return "bad NOT"; case Check::CheckBadCount: return "bad COUNT"; + case Check::CheckBadColon: + return "bad colon"; } llvm_unreachable("unknown FileCheckType"); } @@ -1104,6 +1106,9 @@ if (NextChar == ':') return {Check::CheckPlain, Rest}; + if (NextChar == ' ' || NextChar == '\t') + return {Check::CheckBadColon, Rest}; + if (NextChar != '-') return {Check::CheckNone, StringRef()}; @@ -1119,30 +1124,40 @@ return {Check::FileCheckType(Check::CheckPlain).setCount(Count), Rest}; } - 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}; + const struct { + Check::FileCheckType Type; + const char *Text; + } Suffixes[] { + {Check::CheckNext, "NEXT"}, + {Check::CheckSame, "SAME"}, + {Check::CheckNot, "NOT"}, + {Check::CheckDAG, "DAG"}, + {Check::CheckLabel, "LABEL"}, + {Check::CheckEmpty, "EMPTY"}, + {Check::CheckBadNot, "DAG-NOT"}, + {Check::CheckBadNot, "NEXT-NOT"}, + {Check::CheckBadNot, "SAME-NOT"}, + {Check::CheckBadNot, "EMPTY-NOT"}, + {Check::CheckBadNot, "NOT-DAG"}, + {Check::CheckBadNot, "NOT-NEXT"}, + {Check::CheckBadNot, "NOT-SAME"}, + {Check::CheckBadNot, "NOT-EMPTY"}, + }; - if (Rest.consume_front("DAG:")) - return {Check::CheckDAG, Rest}; + for (const auto &S : Suffixes) { + if (!Rest.consume_front(S.Text)) + continue; - if (Rest.consume_front("LABEL:")) - return {Check::CheckLabel, Rest}; + // You can't combine -NOT with another suffix. + if (S.Type == Check::CheckBadNot) + return {S.Type, Rest}; - if (Rest.consume_front("EMPTY:")) - return {Check::CheckEmpty, Rest}; + if (Rest.consume_front(":")) + return {S.Type, 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("SAME-NOT:") || Rest.startswith("NOT-SAME:") || - Rest.startswith("EMPTY-NOT:") || Rest.startswith("NOT-EMPTY:")) - return {Check::CheckBadNot, Rest}; + if (Rest.consume_front(" ") || Rest.consume_front("\t")) + return {Check::CheckBadColon, Rest}; + } return {Check::CheckNone, Rest}; } @@ -1163,10 +1178,10 @@ /// 2) The found prefix must be followed by a valid check type suffix using \c /// FindCheckType above. /// -/// \returns a pair of StringRefs into the Buffer, which combines: +/// \returns a tuple of StringRefs into the Buffer, which combines: +/// - the beginning of the line leading up to the match, /// - the first match of the regular expression to satisfy these two is -/// returned, -/// otherwise an empty StringRef is returned to indicate failure. +/// returned, otherwise an empty StringRef is returned to indicate failure. /// - buffer rewound to the location right after parsed suffix, for parsing /// to continue from /// @@ -1177,7 +1192,7 @@ /// /// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy /// is unspecified. -static std::pair +static std::tuple FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer, unsigned &LineNumber, Check::FileCheckType &CheckTy) { SmallVector Matches; @@ -1186,7 +1201,7 @@ // Find the first (longest) match using the RE. if (!PrefixRE.match(Buffer, &Matches)) // No match at all, bail. - return {StringRef(), StringRef()}; + return {StringRef(), StringRef(), StringRef()}; StringRef Prefix = Matches[0]; Matches.clear(); @@ -1211,7 +1226,7 @@ // If we've found a valid check type for this prefix, we're done. if (CheckTy != Check::CheckNone) - return {Prefix, AfterSuffix}; + return {Skipped.rsplit('\n').second, Prefix, AfterSuffix}; } // If we didn't successfully find a prefix, we need to skip this invalid @@ -1221,7 +1236,7 @@ } // We ran out of buffer while skipping partial matches so give up. - return {StringRef(), StringRef()}; + return {StringRef(), StringRef(), StringRef()}; } void FileCheckPatternContext::createLineVariable() { @@ -1238,6 +1253,16 @@ FileCheck::~FileCheck() = default; +/// Check whether a given line preamble is likely to be an intentional comment +/// preceeding a check directive. +static bool maybeEndsWithComment(StringRef LineBegin) { + StringRef Preamble = LineBegin.rtrim(" \t"); + if (Preamble.size() == 0) + return false; + char Ending = Preamble.back(); + return StringRef("#/;*!").count(Ending); +} + bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE) { Error DefineError = @@ -1278,9 +1303,10 @@ Check::FileCheckType CheckTy; // See if a prefix occurs in the memory buffer. + StringRef LineBegin; StringRef UsedPrefix; StringRef AfterSuffix; - std::tie(UsedPrefix, AfterSuffix) = + std::tie(LineBegin, UsedPrefix, AfterSuffix) = FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber, CheckTy); if (UsedPrefix.empty()) break; @@ -1291,6 +1317,20 @@ AfterSuffix.data() < Buffer.data() + Buffer.size() && "Parsing after suffix doesn't start inside of buffer!"); + // Complain about directives that are not followed immediately by a colon. + if (CheckTy == Check::CheckBadColon) { + if (!maybeEndsWithComment(LineBegin)) { + // Skip forward in the buffer, and ignore this occurrence, since it's + // clearly not an intentional CHECK comment. + assert(!AfterSuffix.empty()); + Buffer = AfterSuffix; + continue; + } + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error, + "colon required after check directive '" + UsedPrefix + "'"); + return true; + } + // Location to use for error messages. const char *UsedPrefixStart = UsedPrefix.data(); diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp --- a/llvm/utils/FileCheck/FileCheck.cpp +++ b/llvm/utils/FileCheck/FileCheck.cpp @@ -287,6 +287,8 @@ return "bad-not"; case Check::CheckBadCount: return "bad-count"; + case Check::CheckBadColon: + return "bad-colon"; case Check::CheckNone: llvm_unreachable("invalid FileCheckType"); }