//
//===----------------------------------------------------------------------===//
-#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Regex.h"
+#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
-#include "llvm/Support/Signals.h"
-#include "llvm/Support/system_error.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/StringMap.h"
#include <algorithm>
+#include <cctype>
#include <map>
#include <string>
+#include <system_error>
#include <vector>
using namespace llvm;
InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
cl::init("-"), cl::value_desc("filename"));
-static cl::opt<std::string>
-CheckPrefix("check-prefix", cl::init("CHECK"),
- cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
+static cl::list<std::string>
+CheckPrefixes("check-prefix",
+ cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
static cl::opt<bool>
NoCanonicalizeWhiteSpace("strict-whitespace",
cl::desc("Do not treat all horizontal whitespace as equivalent"));
+static cl::list<std::string> ImplicitCheckNot(
+ "implicit-check-not",
+ cl::desc("Add an implicit negative check with this pattern to every\n"
+ "positive check. This can be used to ensure that no instances of\n"
+ "this pattern occur which are not matched by a positive pattern"),
+ cl::value_desc("pattern"));
+
+static cl::opt<bool> AllowEmptyInput(
+ "allow-empty", cl::init(false),
+ cl::desc("Allow the input file to be empty. This is useful when making\n"
+ "checks that some error message does not occur, for example."));
+
+typedef cl::list<std::string>::const_iterator prefix_iterator;
+
//===----------------------------------------------------------------------===//
// Pattern Handling Code.
//===----------------------------------------------------------------------===//
+namespace Check {
+ enum CheckType {
+ CheckNone = 0,
+ CheckPlain,
+ CheckNext,
+ CheckNot,
+ CheckDAG,
+ CheckLabel,
+
+ /// MatchEOF - When set, this pattern only matches the end of file. This is
+ /// used for trailing CHECK-NOTs.
+ CheckEOF
+ };
+}
+
class Pattern {
SMLoc PatternLoc;
- /// MatchEOF - When set, this pattern only matches the end of file. This is
- /// used for trailing CHECK-NOTs.
- bool MatchEOF;
+ Check::CheckType CheckTy;
/// FixedStr - If non-empty, this pattern is a fixed string match with the
/// specified fixed string.
public:
- Pattern(bool matchEOF = false) : MatchEOF(matchEOF) { }
+ Pattern(Check::CheckType Ty)
+ : CheckTy(Ty) { }
- /// ParsePattern - Parse the given string into the Pattern. SM provides the
- /// SourceMgr used for error reports, and LineNumber is the line number in
- /// the input file from which the pattern string was read.
- /// Returns true in case of an error, false otherwise.
- bool ParsePattern(StringRef PatternStr, SourceMgr &SM, unsigned LineNumber);
+ /// getLoc - Return the location in source code.
+ SMLoc getLoc() const { return PatternLoc; }
+
+ /// ParsePattern - Parse the given string into the Pattern. Prefix provides
+ /// which prefix is being matched, SM provides the SourceMgr used for error
+ /// reports, and LineNumber is the line number in the input file from which
+ /// the pattern string was read. Returns true in case of an error, false
+ /// otherwise.
+ bool ParsePattern(StringRef PatternStr,
+ StringRef Prefix,
+ SourceMgr &SM,
+ unsigned LineNumber);
/// Match - Match the pattern string against the input buffer Buffer. This
/// returns the position that is matched or npos if there is no match. If
void PrintFailureInfo(const SourceMgr &SM, StringRef Buffer,
const StringMap<StringRef> &VariableTable) const;
+ bool hasVariable() const { return !(VariableUses.empty() &&
+ VariableDefs.empty()); }
+
+ Check::CheckType getCheckTy() const { return CheckTy; }
+
private:
- static void AddFixedStringToRegEx(StringRef FixedStr, std::string &TheStr);
bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM);
void AddBackrefToRegEx(unsigned BackrefNum);
/// \brief Evaluates expression and stores the result to \p Value.
/// \return true on success. false when the expression has invalid syntax.
bool EvaluateExpression(StringRef Expr, std::string &Value) const;
+
+ /// \brief Finds the closing sequence of a regex variable usage or
+ /// definition. Str has to point in the beginning of the definition
+ /// (right after the opening sequence).
+ /// \return offset of the closing sequence within Str, or npos if it was not
+ /// found.
+ size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
};
-bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM,
+bool Pattern::ParsePattern(StringRef PatternStr,
+ StringRef Prefix,
+ SourceMgr &SM,
unsigned LineNumber) {
this->LineNumber = LineNumber;
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
if (PatternStr.empty()) {
SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
"found empty check string with prefix '" +
- CheckPrefix+":'");
+ Prefix + ":'");
return true;
}
// itself must be of the form "[a-zA-Z_][0-9a-zA-Z_]*", otherwise we reject
// it. This is to catch some common errors.
if (PatternStr.startswith("[[")) {
- // Verify that it is terminated properly.
- size_t End = PatternStr.find("]]");
+ // Find the closing bracket pair ending the match. End is going to be an
+ // offset relative to the beginning of the match string.
+ size_t End = FindRegexVarEnd(PatternStr.substr(2), SM);
+
if (End == StringRef::npos) {
SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
SourceMgr::DK_Error,
return true;
}
- StringRef MatchStr = PatternStr.substr(2, End-2);
- PatternStr = PatternStr.substr(End+2);
+ StringRef MatchStr = PatternStr.substr(2, End);
+ PatternStr = PatternStr.substr(End+4);
// Get the regex name (e.g. "foo").
size_t NameEnd = MatchStr.find(':');
}
// Name can't start with a digit.
- if (isdigit(Name[0])) {
+ if (isdigit(static_cast<unsigned char>(Name[0]))) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
"invalid name in named regex");
return true;
// Find the end, which is the start of the next regex.
size_t FixedMatchEnd = PatternStr.find("{{");
FixedMatchEnd = std::min(FixedMatchEnd, PatternStr.find("[["));
- AddFixedStringToRegEx(PatternStr.substr(0, FixedMatchEnd), RegExStr);
+ RegExStr += Regex::escape(PatternStr.substr(0, FixedMatchEnd));
PatternStr = PatternStr.substr(FixedMatchEnd);
}
return false;
}
-void Pattern::AddFixedStringToRegEx(StringRef FixedStr, std::string &TheStr) {
- // Add the characters from FixedStr to the regex, escaping as needed. This
- // avoids "leaning toothpicks" in common patterns.
- for (unsigned i = 0, e = FixedStr.size(); i != e; ++i) {
- switch (FixedStr[i]) {
- // These are the special characters matched in "p_ere_exp".
- case '(':
- case ')':
- case '^':
- case '$':
- case '|':
- case '*':
- case '+':
- case '?':
- case '.':
- case '[':
- case '\\':
- case '{':
- TheStr += '\\';
- // FALL THROUGH.
- default:
- TheStr += FixedStr[i];
- break;
- }
- }
-}
-
bool Pattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen,
SourceMgr &SM) {
Regex R(RS);
size_t Pattern::Match(StringRef Buffer, size_t &MatchLen,
StringMap<StringRef> &VariableTable) const {
// If this is the EOF pattern, match it immediately.
- if (MatchEOF) {
+ if (CheckTy == Check::CheckEOF) {
MatchLen = 0;
return Buffer.size();
}
if (it == VariableTable.end())
return StringRef::npos;
- // Look up the value and escape it so that we can plop it into the regex.
- AddFixedStringToRegEx(it->second, Value);
+ // Look up the value and escape it so that we can put it into the regex.
+ Value += Regex::escape(it->second);
}
// Plop it into the regex at the adjusted offset.
}
}
+size_t Pattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {
+ // Offset keeps track of the current offset within the input Str
+ size_t Offset = 0;
+ // [...] Nesting depth
+ size_t BracketDepth = 0;
+
+ while (!Str.empty()) {
+ if (Str.startswith("]]") && BracketDepth == 0)
+ return Offset;
+ if (Str[0] == '\\') {
+ // Backslash escapes the next char within regexes, so skip them both.
+ Str = Str.substr(2);
+ Offset += 2;
+ } else {
+ switch (Str[0]) {
+ default:
+ break;
+ case '[':
+ BracketDepth++;
+ break;
+ case ']':
+ if (BracketDepth == 0) {
+ SM.PrintMessage(SMLoc::getFromPointer(Str.data()),
+ SourceMgr::DK_Error,
+ "missing closing \"]\" for regex variable");
+ exit(1);
+ }
+ BracketDepth--;
+ break;
+ }
+ Str = Str.substr(1);
+ Offset++;
+ }
+ }
+
+ return StringRef::npos;
+}
+
+
//===----------------------------------------------------------------------===//
// Check Strings.
//===----------------------------------------------------------------------===//
/// Pat - The pattern to match.
Pattern Pat;
+ /// Prefix - Which prefix name this check matched.
+ StringRef Prefix;
+
/// Loc - The location in the match file that the check string was specified.
SMLoc Loc;
- /// IsCheckNext - This is true if this is a CHECK-NEXT: directive (as opposed
- /// to a CHECK: directive.
- bool IsCheckNext;
+ /// CheckTy - Specify what kind of check this is. e.g. CHECK-NEXT: directive,
+ /// as opposed to a CHECK: directive.
+ Check::CheckType CheckTy;
- /// NotStrings - These are all of the strings that are disallowed from
+ /// 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<std::pair<SMLoc, Pattern> > NotStrings;
+ std::vector<Pattern> DagNotStrings;
+
+
+ CheckString(const Pattern &P,
+ StringRef S,
+ SMLoc L,
+ Check::CheckType Ty)
+ : Pat(P), Prefix(S), Loc(L), CheckTy(Ty) {}
+
+ /// Check - Match check string and its "not strings" and/or "dag strings".
+ size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode,
+ 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;
+
+ /// CheckNot - Verify there's no "not strings" in the given buffer.
+ bool CheckNot(const SourceMgr &SM, StringRef Buffer,
+ const std::vector<const Pattern *> &NotStrings,
+ StringMap<StringRef> &VariableTable) const;
- CheckString(const Pattern &P, SMLoc L, bool isCheckNext)
- : Pat(P), Loc(L), IsCheckNext(isCheckNext) {}
+ /// CheckDag - Match "dag strings" and their mixed "not strings".
+ size_t CheckDag(const SourceMgr &SM, StringRef Buffer,
+ std::vector<const Pattern *> &NotStrings,
+ StringMap<StringRef> &VariableTable) const;
};
-/// CanonicalizeInputFile - Remove duplicate horizontal space from the specified
-/// memory buffer, free it, and return a new one.
-static MemoryBuffer *CanonicalizeInputFile(MemoryBuffer *MB) {
+/// Canonicalize whitespaces in the input file. Line endings are replaced
+/// with UNIX-style '\n'.
+///
+/// \param PreserveHorizontal Don't squash consecutive horizontal whitespace
+/// characters to a single space.
+static std::unique_ptr<MemoryBuffer>
+CanonicalizeInputFile(std::unique_ptr<MemoryBuffer> MB,
+ bool PreserveHorizontal) {
SmallString<128> NewFile;
NewFile.reserve(MB->getBufferSize());
continue;
}
- // If current char is not a horizontal whitespace, dump it to output as is.
- if (*Ptr != ' ' && *Ptr != '\t') {
+ // If current char is not a horizontal whitespace or if horizontal
+ // whitespace canonicalization is disabled, dump it to output as is.
+ if (PreserveHorizontal || (*Ptr != ' ' && *Ptr != '\t')) {
NewFile.push_back(*Ptr);
continue;
}
++Ptr;
}
- // Free the old buffer and return a new one.
- MemoryBuffer *MB2 =
- MemoryBuffer::getMemBufferCopy(NewFile.str(), MB->getBufferIdentifier());
+ return std::unique_ptr<MemoryBuffer>(
+ MemoryBuffer::getMemBufferCopy(NewFile.str(), MB->getBufferIdentifier()));
+}
+
+static bool IsPartOfWord(char c) {
+ return (isalnum(c) || c == '-' || c == '_');
+}
+
+// Get the size of the prefix extension.
+static size_t CheckTypeSize(Check::CheckType Ty) {
+ switch (Ty) {
+ case Check::CheckNone:
+ return 0;
+
+ case Check::CheckPlain:
+ return sizeof(":") - 1;
+
+ case Check::CheckNext:
+ return sizeof("-NEXT:") - 1;
+
+ case Check::CheckNot:
+ return sizeof("-NOT:") - 1;
+
+ case Check::CheckDAG:
+ return sizeof("-DAG:") - 1;
+
+ case Check::CheckLabel:
+ return sizeof("-LABEL:") - 1;
+
+ case Check::CheckEOF:
+ llvm_unreachable("Should not be using EOF size");
+ }
+
+ llvm_unreachable("Bad check type");
+}
+
+static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) {
+ char NextChar = Buffer[Prefix.size()];
+
+ // Verify that the : is present after the prefix.
+ if (NextChar == ':')
+ return Check::CheckPlain;
+
+ if (NextChar != '-')
+ return Check::CheckNone;
+
+ StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
+ if (Rest.startswith("NEXT:"))
+ return Check::CheckNext;
+
+ if (Rest.startswith("NOT:"))
+ return Check::CheckNot;
+
+ if (Rest.startswith("DAG:"))
+ return Check::CheckDAG;
+
+ if (Rest.startswith("LABEL:"))
+ return Check::CheckLabel;
+
+ return Check::CheckNone;
+}
+
+// From the given position, find the next character after the word.
+static size_t SkipWord(StringRef Str, size_t Loc) {
+ while (Loc < Str.size() && IsPartOfWord(Str[Loc]))
+ ++Loc;
+ return Loc;
+}
+
+// Try to find the first match in buffer for any prefix. If a valid match is
+// found, return that prefix and set its type and location. If there are almost
+// matches (e.g. the actual prefix string is found, but is not an actual check
+// string), but no valid match, return an empty string and set the position to
+// resume searching from. If no partial matches are found, return an empty
+// string and the location will be StringRef::npos. If one prefix is a substring
+// of another, the maximal match should be found. e.g. if "A" and "AA" are
+// prefixes then AA-CHECK: should match the second one.
+static StringRef FindFirstCandidateMatch(StringRef &Buffer,
+ Check::CheckType &CheckTy,
+ size_t &CheckLoc) {
+ StringRef FirstPrefix;
+ size_t FirstLoc = StringRef::npos;
+ size_t SearchLoc = StringRef::npos;
+ Check::CheckType FirstTy = Check::CheckNone;
+
+ CheckTy = Check::CheckNone;
+ CheckLoc = StringRef::npos;
+
+ for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end();
+ I != E; ++I) {
+ StringRef Prefix(*I);
+ size_t PrefixLoc = Buffer.find(Prefix);
+
+ if (PrefixLoc == StringRef::npos)
+ continue;
+
+ // Track where we are searching for invalid prefixes that look almost right.
+ // We need to only advance to the first partial match on the next attempt
+ // since a partial match could be a substring of a later, valid prefix.
+ // Need to skip to the end of the word, otherwise we could end up
+ // matching a prefix in a substring later.
+ if (PrefixLoc < SearchLoc)
+ SearchLoc = SkipWord(Buffer, PrefixLoc);
+
+ // We only want to find the first match to avoid skipping some.
+ if (PrefixLoc > FirstLoc)
+ continue;
+ // If one matching check-prefix is a prefix of another, choose the
+ // longer one.
+ if (PrefixLoc == FirstLoc && Prefix.size() < FirstPrefix.size())
+ continue;
+
+ StringRef Rest = Buffer.drop_front(PrefixLoc);
+ // Make sure we have actually found the prefix, and not a word containing
+ // it. This should also prevent matching the wrong prefix when one is a
+ // substring of another.
+ if (PrefixLoc != 0 && IsPartOfWord(Buffer[PrefixLoc - 1]))
+ FirstTy = Check::CheckNone;
+ else
+ FirstTy = FindCheckType(Rest, Prefix);
+
+ FirstLoc = PrefixLoc;
+ FirstPrefix = Prefix;
+ }
+
+ // If the first prefix is invalid, we should continue the search after it.
+ if (FirstTy == Check::CheckNone) {
+ CheckLoc = SearchLoc;
+ return "";
+ }
- delete MB;
- return MB2;
+ CheckTy = FirstTy;
+ CheckLoc = FirstLoc;
+ return FirstPrefix;
}
+static StringRef FindFirstMatchingPrefix(StringRef &Buffer,
+ unsigned &LineNumber,
+ Check::CheckType &CheckTy,
+ size_t &CheckLoc) {
+ while (!Buffer.empty()) {
+ StringRef Prefix = FindFirstCandidateMatch(Buffer, CheckTy, CheckLoc);
+ // If we found a real match, we are done.
+ if (!Prefix.empty()) {
+ LineNumber += Buffer.substr(0, CheckLoc).count('\n');
+ return Prefix;
+ }
+
+ // We didn't find any almost matches either, we are also done.
+ if (CheckLoc == StringRef::npos)
+ return StringRef();
+
+ LineNumber += Buffer.substr(0, CheckLoc + 1).count('\n');
+
+ // Advance to the last possible match we found and try again.
+ Buffer = Buffer.drop_front(CheckLoc + 1);
+ }
+
+ return StringRef();
+}
/// ReadCheckFile - Read the check file, which specifies the sequence of
/// expected strings. The strings are added to the CheckStrings vector.
/// Returns true in case of an error, false otherwise.
static bool ReadCheckFile(SourceMgr &SM,
std::vector<CheckString> &CheckStrings) {
- OwningPtr<MemoryBuffer> File;
- if (error_code ec =
- MemoryBuffer::getFileOrSTDIN(CheckFilename.c_str(), File)) {
- errs() << "Could not open check file '" << CheckFilename << "': "
- << ec.message() << '\n';
+ ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
+ MemoryBuffer::getFileOrSTDIN(CheckFilename);
+ if (std::error_code EC = FileOrErr.getError()) {
+ errs() << "Could not open check file '" << CheckFilename
+ << "': " << EC.message() << '\n';
return true;
}
- MemoryBuffer *F = File.take();
// If we want to canonicalize whitespace, strip excess whitespace from the
- // buffer containing the CHECK lines.
- if (!NoCanonicalizeWhiteSpace)
- F = CanonicalizeInputFile(F);
-
- SM.AddNewSourceBuffer(F, SMLoc());
+ // buffer containing the CHECK lines. Remove DOS style line endings.
+ std::unique_ptr<MemoryBuffer> F = CanonicalizeInputFile(
+ std::move(FileOrErr.get()), NoCanonicalizeWhiteSpace);
// Find all instances of CheckPrefix followed by : in the file.
StringRef Buffer = F->getBuffer();
- std::vector<std::pair<SMLoc, Pattern> > NotMatches;
+
+ SM.AddNewSourceBuffer(std::move(F), SMLoc());
+
+ std::vector<Pattern> ImplicitNegativeChecks;
+ for (const auto &PatternString : ImplicitCheckNot) {
+ // Create a buffer with fake command line content in order to display the
+ // command line option responsible for the specific implicit CHECK-NOT.
+ std::string Prefix = std::string("-") + ImplicitCheckNot.ArgStr + "='";
+ std::string Suffix = "'";
+ std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy(
+ Prefix + PatternString + Suffix, "command line");
+
+ StringRef PatternInBuffer =
+ CmdLine->getBuffer().substr(Prefix.size(), PatternString.size());
+ SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
+
+ ImplicitNegativeChecks.push_back(Pattern(Check::CheckNot));
+ ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer,
+ "IMPLICIT-CHECK", SM, 0);
+ }
+
+
+ std::vector<Pattern> DagNotMatches = ImplicitNegativeChecks;
// LineNumber keeps track of the line on which CheckPrefix instances are
// found.
unsigned LineNumber = 1;
while (1) {
- // See if Prefix occurs in the memory buffer.
- size_t PrefixLoc = Buffer.find(CheckPrefix);
- // If we didn't find a match, we're done.
- if (PrefixLoc == StringRef::npos)
+ Check::CheckType CheckTy;
+ size_t PrefixLoc;
+
+ // See if a prefix occurs in the memory buffer.
+ StringRef UsedPrefix = FindFirstMatchingPrefix(Buffer,
+ LineNumber,
+ CheckTy,
+ PrefixLoc);
+ if (UsedPrefix.empty())
break;
- LineNumber += Buffer.substr(0, PrefixLoc).count('\n');
-
- Buffer = Buffer.substr(PrefixLoc);
+ Buffer = Buffer.drop_front(PrefixLoc);
- const char *CheckPrefixStart = Buffer.data();
+ // Location to use for error messages.
+ const char *UsedPrefixStart = Buffer.data() + (PrefixLoc == 0 ? 0 : 1);
- // When we find a check prefix, keep track of whether we find CHECK: or
- // CHECK-NEXT:
- bool IsCheckNext = false, IsCheckNot = false;
+ // PrefixLoc is to the start of the prefix. Skip to the end.
+ Buffer = Buffer.drop_front(UsedPrefix.size() + CheckTypeSize(CheckTy));
- // Verify that the : is present after the prefix.
- if (Buffer[CheckPrefix.size()] == ':') {
- Buffer = Buffer.substr(CheckPrefix.size()+1);
- } else if (Buffer.size() > CheckPrefix.size()+6 &&
- memcmp(Buffer.data()+CheckPrefix.size(), "-NEXT:", 6) == 0) {
- Buffer = Buffer.substr(CheckPrefix.size()+6);
- IsCheckNext = true;
- } else if (Buffer.size() > CheckPrefix.size()+5 &&
- memcmp(Buffer.data()+CheckPrefix.size(), "-NOT:", 5) == 0) {
- Buffer = Buffer.substr(CheckPrefix.size()+5);
- IsCheckNot = true;
- } else {
- Buffer = Buffer.substr(1);
- continue;
- }
-
- // Okay, we found the prefix, yay. Remember the rest of the line, but
- // ignore leading and trailing whitespace.
+ // Okay, we found the prefix, yay. Remember the rest of the line, but ignore
+ // leading and trailing whitespace.
Buffer = Buffer.substr(Buffer.find_first_not_of(" \t"));
// Scan ahead to the end of line.
SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
// Parse the pattern.
- Pattern P;
- if (P.ParsePattern(Buffer.substr(0, EOL), SM, LineNumber))
+ Pattern P(CheckTy);
+ if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber))
return true;
+ // Verify that CHECK-LABEL lines do not define or use variables
+ if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {
+ SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
+ SourceMgr::DK_Error,
+ "found '" + UsedPrefix + "-LABEL:'"
+ " with variable definition or use");
+ return true;
+ }
+
Buffer = Buffer.substr(EOL);
// Verify that CHECK-NEXT lines have at least one CHECK line before them.
- if (IsCheckNext && CheckStrings.empty()) {
- SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart),
+ if ((CheckTy == Check::CheckNext) && CheckStrings.empty()) {
+ SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
SourceMgr::DK_Error,
- "found '"+CheckPrefix+"-NEXT:' without previous '"+
- CheckPrefix+ ": line");
+ "found '" + UsedPrefix + "-NEXT:' without previous '"
+ + UsedPrefix + ": line");
return true;
}
- // Handle CHECK-NOT.
- if (IsCheckNot) {
- NotMatches.push_back(std::make_pair(SMLoc::getFromPointer(Buffer.data()),
- P));
+ // Handle CHECK-DAG/-NOT.
+ if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) {
+ DagNotMatches.push_back(P);
continue;
}
// Okay, add the string we captured to the output vector and move on.
CheckStrings.push_back(CheckString(P,
+ UsedPrefix,
PatternLoc,
- IsCheckNext));
- std::swap(NotMatches, CheckStrings.back().NotStrings);
+ CheckTy));
+ std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
+ DagNotMatches = ImplicitNegativeChecks;
}
- // Add an EOF pattern for any trailing CHECK-NOTs.
- if (!NotMatches.empty()) {
- CheckStrings.push_back(CheckString(Pattern(true),
+ // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first
+ // prefix as a filler for the error message.
+ if (!DagNotMatches.empty()) {
+ CheckStrings.push_back(CheckString(Pattern(Check::CheckEOF),
+ CheckPrefixes[0],
SMLoc::getFromPointer(Buffer.data()),
- false));
- std::swap(NotMatches, CheckStrings.back().NotStrings);
+ Check::CheckEOF));
+ std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
}
if (CheckStrings.empty()) {
- errs() << "error: no check strings found with prefix '" << CheckPrefix
- << ":'\n";
+ errs() << "error: no check strings found with prefix"
+ << (CheckPrefixes.size() > 1 ? "es " : " ");
+ for (size_t I = 0, N = CheckPrefixes.size(); I != N; ++I) {
+ StringRef Prefix(CheckPrefixes[I]);
+ errs() << '\'' << Prefix << ":'";
+ if (I != N - 1)
+ errs() << ", ";
+ }
+
+ errs() << '\n';
return true;
}
return false;
}
-static void PrintCheckFailed(const SourceMgr &SM, const CheckString &CheckStr,
- StringRef Buffer,
+static void PrintCheckFailed(const SourceMgr &SM, const SMLoc &Loc,
+ const Pattern &Pat, StringRef Buffer,
StringMap<StringRef> &VariableTable) {
// Otherwise, we have an error, emit an error message.
- SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error,
+ SM.PrintMessage(Loc, SourceMgr::DK_Error,
"expected string not found in input");
// Print the "scanning from here" line. If the current position is at the
"scanning from here");
// Allow the pattern to print additional information if desired.
- CheckStr.Pat.PrintFailureInfo(SM, Buffer, VariableTable);
+ Pat.PrintFailureInfo(SM, Buffer, VariableTable);
+}
+
+static void PrintCheckFailed(const SourceMgr &SM, const CheckString &CheckStr,
+ StringRef Buffer,
+ StringMap<StringRef> &VariableTable) {
+ PrintCheckFailed(SM, CheckStr.Loc, CheckStr.Pat, Buffer, VariableTable);
}
/// CountNumNewlinesBetween - Count the number of newlines in the specified
/// range.
-static unsigned CountNumNewlinesBetween(StringRef Range) {
+static unsigned CountNumNewlinesBetween(StringRef Range,
+ const char *&FirstNewLine) {
unsigned NumNewLines = 0;
while (1) {
// Scan for newline.
(Range[0] != Range[1]))
Range = Range.substr(1);
Range = Range.substr(1);
+
+ if (NumNewLines == 1)
+ FirstNewLine = Range.begin();
+ }
+}
+
+size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer,
+ bool IsLabelScanMode, size_t &MatchLen,
+ StringMap<StringRef> &VariableTable) const {
+ size_t LastPos = 0;
+ std::vector<const Pattern *> NotStrings;
+
+ // IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL
+ // bounds; we have not processed variable definitions within the bounded block
+ // yet so cannot handle any final CHECK-DAG yet; this is handled when going
+ // over the block again (including the last CHECK-LABEL) in normal mode.
+ if (!IsLabelScanMode) {
+ // 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);
+ size_t MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable);
+ if (MatchPos == StringRef::npos) {
+ PrintCheckFailed(SM, *this, MatchBuffer, VariableTable);
+ return StringRef::npos;
+ }
+ MatchPos += LastPos;
+
+ // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
+ // or CHECK-NOT
+ if (!IsLabelScanMode) {
+ 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 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;
+}
+
+bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
+ if (CheckTy != Check::CheckNext)
+ return false;
+
+ // 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");
+
+ const char *FirstNewLine = nullptr;
+ unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
+
+ if (NumNewLines == 0) {
+ SM.PrintMessage(Loc, SourceMgr::DK_Error, Prefix +
+ "-NEXT: 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,
+ "previous match ended here");
+ return true;
+ }
+
+ if (NumNewLines != 1) {
+ SM.PrintMessage(Loc, SourceMgr::DK_Error, Prefix +
+ "-NEXT: 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,
+ "previous match ended here");
+ SM.PrintMessage(SMLoc::getFromPointer(FirstNewLine), SourceMgr::DK_Note,
+ "non-matching line after previous match is here");
+ return true;
+ }
+
+ return false;
+}
+
+bool CheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
+ const std::vector<const Pattern *> &NotStrings,
+ StringMap<StringRef> &VariableTable) const {
+ for (unsigned ChunkNo = 0, e = NotStrings.size();
+ ChunkNo != e; ++ChunkNo) {
+ const Pattern *Pat = NotStrings[ChunkNo];
+ assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
+
+ size_t MatchLen = 0;
+ size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable);
+
+ if (Pos == StringRef::npos) continue;
+
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()+Pos),
+ SourceMgr::DK_Error,
+ Prefix + "-NOT: string occurred!");
+ SM.PrintMessage(Pat->getLoc(), SourceMgr::DK_Note,
+ Prefix + "-NOT: pattern specified here");
+ return true;
}
+
+ return false;
+}
+
+size_t CheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
+ std::vector<const Pattern *> &NotStrings,
+ StringMap<StringRef> &VariableTable) const {
+ if (DagNotStrings.empty())
+ return 0;
+
+ size_t LastPos = 0;
+ size_t StartPos = LastPos;
+
+ for (unsigned ChunkNo = 0, e = DagNotStrings.size();
+ ChunkNo != e; ++ChunkNo) {
+ const Pattern &Pat = DagNotStrings[ChunkNo];
+
+ assert((Pat.getCheckTy() == Check::CheckDAG ||
+ Pat.getCheckTy() == Check::CheckNot) &&
+ "Invalid CHECK-DAG or CHECK-NOT!");
+
+ if (Pat.getCheckTy() == Check::CheckNot) {
+ NotStrings.push_back(&Pat);
+ continue;
+ }
+
+ assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!");
+
+ size_t MatchLen = 0, MatchPos;
+
+ // CHECK-DAG always matches from the start.
+ StringRef MatchBuffer = Buffer.substr(StartPos);
+ MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable);
+ // With a group of CHECK-DAGs, a single mismatching means the match on
+ // that group of CHECK-DAGs fails immediately.
+ if (MatchPos == StringRef::npos) {
+ PrintCheckFailed(SM, Pat.getLoc(), Pat, MatchBuffer, VariableTable);
+ return StringRef::npos;
+ }
+ // Re-calc it as the offset relative to the start of the original string.
+ MatchPos += StartPos;
+
+ if (!NotStrings.empty()) {
+ if (MatchPos < LastPos) {
+ // Reordered?
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + MatchPos),
+ SourceMgr::DK_Error,
+ Prefix + "-DAG: found a match of CHECK-DAG"
+ " reordering across a CHECK-NOT");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + LastPos),
+ SourceMgr::DK_Note,
+ Prefix + "-DAG: the farthest match of CHECK-DAG"
+ " is found here");
+ SM.PrintMessage(NotStrings[0]->getLoc(), SourceMgr::DK_Note,
+ Prefix + "-NOT: the crossed pattern specified"
+ " here");
+ SM.PrintMessage(Pat.getLoc(), SourceMgr::DK_Note,
+ Prefix + "-DAG: the reordered pattern specified"
+ " here");
+ return StringRef::npos;
+ }
+ // All subsequent CHECK-DAGs should be matched from the farthest
+ // position of all precedent CHECK-DAGs (including this one.)
+ StartPos = LastPos;
+ // If there's CHECK-NOTs between two CHECK-DAGs or from CHECK to
+ // CHECK-DAG, verify that there's no 'not' strings occurred in that
+ // region.
+ StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
+ if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable))
+ return StringRef::npos;
+ // Clear "not strings".
+ NotStrings.clear();
+ }
+
+ // Update the last position with CHECK-DAG matches.
+ LastPos = std::max(MatchPos + MatchLen, LastPos);
+ }
+
+ return LastPos;
+}
+
+// A check prefix must contain only alphanumeric, hyphens and underscores.
+static bool ValidateCheckPrefix(StringRef CheckPrefix) {
+ Regex Validator("^[a-zA-Z0-9_-]*$");
+ return Validator.match(CheckPrefix);
+}
+
+static bool ValidateCheckPrefixes() {
+ StringSet<> PrefixSet;
+
+ for (prefix_iterator I = CheckPrefixes.begin(), E = CheckPrefixes.end();
+ I != E; ++I) {
+ StringRef Prefix(*I);
+
+ // Reject empty prefixes.
+ if (Prefix == "")
+ return false;
+
+ if (!PrefixSet.insert(Prefix).second)
+ return false;
+
+ if (!ValidateCheckPrefix(Prefix))
+ return false;
+ }
+
+ return true;
+}
+
+// I don't think there's a way to specify an initial value for cl::list,
+// so if nothing was specified, add the default
+static void AddCheckPrefixIfNeeded() {
+ if (CheckPrefixes.empty())
+ CheckPrefixes.push_back("CHECK");
}
int main(int argc, char **argv) {
PrettyStackTraceProgram X(argc, argv);
cl::ParseCommandLineOptions(argc, argv);
+ if (!ValidateCheckPrefixes()) {
+ errs() << "Supplied check-prefix is invalid! Prefixes must be unique and "
+ "start with a letter and contain only alphanumeric characters, "
+ "hyphens and underscores\n";
+ return 2;
+ }
+
+ AddCheckPrefixIfNeeded();
+
SourceMgr SM;
// Read the expected strings from the check file.
return 2;
// Open the file to check and add it to SourceMgr.
- OwningPtr<MemoryBuffer> File;
- if (error_code ec =
- MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), File)) {
- errs() << "Could not open input file '" << InputFilename << "': "
- << ec.message() << '\n';
+ ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
+ MemoryBuffer::getFileOrSTDIN(InputFilename);
+ if (std::error_code EC = FileOrErr.getError()) {
+ errs() << "Could not open input file '" << InputFilename
+ << "': " << EC.message() << '\n';
return 2;
}
- MemoryBuffer *F = File.take();
+ std::unique_ptr<MemoryBuffer> &File = FileOrErr.get();
- if (F->getBufferSize() == 0) {
+ if (File->getBufferSize() == 0 && !AllowEmptyInput) {
errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
return 2;
}
-
- // Remove duplicate spaces in the input file if requested.
- if (!NoCanonicalizeWhiteSpace)
- F = CanonicalizeInputFile(F);
- SM.AddNewSourceBuffer(F, SMLoc());
-
- /// VariableTable - This holds all the current filecheck variables.
- StringMap<StringRef> VariableTable;
+ // Remove duplicate spaces in the input file if requested.
+ // Remove DOS style line endings.
+ std::unique_ptr<MemoryBuffer> F =
+ CanonicalizeInputFile(std::move(File), NoCanonicalizeWhiteSpace);
// Check that we have all of the expected strings, in order, in the input
// file.
StringRef Buffer = F->getBuffer();
- const char *LastMatch = Buffer.data();
-
- for (unsigned StrNo = 0, e = CheckStrings.size(); StrNo != e; ++StrNo) {
- const CheckString &CheckStr = CheckStrings[StrNo];
+ SM.AddNewSourceBuffer(std::move(F), SMLoc());
- StringRef SearchFrom = Buffer;
-
- // Find StrNo in the file.
- size_t MatchLen = 0;
- size_t MatchPos = CheckStr.Pat.Match(Buffer, MatchLen, VariableTable);
- Buffer = Buffer.substr(MatchPos);
+ /// VariableTable - This holds all the current filecheck variables.
+ StringMap<StringRef> VariableTable;
- // If we didn't find a match, reject the input.
- if (MatchPos == StringRef::npos) {
- PrintCheckFailed(SM, CheckStr, SearchFrom, VariableTable);
- return 1;
- }
+ bool hasError = false;
- StringRef SkippedRegion(LastMatch, Buffer.data()-LastMatch);
+ unsigned i = 0, j = 0, e = CheckStrings.size();
- // 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 (CheckStr.IsCheckNext) {
- // Count the number of newlines between the previous match and this one.
- assert(LastMatch != F->getBufferStart() &&
- "CHECK-NEXT can't be the first check in a file");
-
- unsigned NumNewLines = CountNumNewlinesBetween(SkippedRegion);
- if (NumNewLines == 0) {
- SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error,
- CheckPrefix+"-NEXT: is on the same line as previous match");
- SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
- SourceMgr::DK_Note, "'next' match was here");
- SM.PrintMessage(SMLoc::getFromPointer(LastMatch), SourceMgr::DK_Note,
- "previous match was here");
- return 1;
+ while (true) {
+ StringRef CheckRegion;
+ if (j == e) {
+ CheckRegion = Buffer;
+ } else {
+ const CheckString &CheckLabelStr = CheckStrings[j];
+ if (CheckLabelStr.CheckTy != Check::CheckLabel) {
+ ++j;
+ continue;
}
- if (NumNewLines != 1) {
- SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error, CheckPrefix+
- "-NEXT: is not on the line after the previous match");
- SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
- SourceMgr::DK_Note, "'next' match was here");
- SM.PrintMessage(SMLoc::getFromPointer(LastMatch), SourceMgr::DK_Note,
- "previous match was here");
- return 1;
+ // 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;
}
+
+ CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen);
+ Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen);
+ ++j;
}
- // If this match had "not strings", verify that they don't exist in the
- // skipped region.
- for (unsigned ChunkNo = 0, e = CheckStr.NotStrings.size();
- ChunkNo != e; ++ChunkNo) {
+ 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 Pos = CheckStr.NotStrings[ChunkNo].second.Match(SkippedRegion,
- MatchLen,
- VariableTable);
- if (Pos == StringRef::npos) continue;
-
- SM.PrintMessage(SMLoc::getFromPointer(LastMatch+Pos), SourceMgr::DK_Error,
- CheckPrefix+"-NOT: string occurred!");
- SM.PrintMessage(CheckStr.NotStrings[ChunkNo].first, SourceMgr::DK_Note,
- CheckPrefix+"-NOT: pattern specified here");
- return 1;
- }
+ size_t MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen,
+ VariableTable);
+
+ if (MatchPos == StringRef::npos) {
+ hasError = true;
+ i = j;
+ break;
+ }
+ CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
+ }
- // Otherwise, everything is good. Step over the matched text and remember
- // the position after the match as the end of the last match.
- Buffer = Buffer.substr(MatchLen);
- LastMatch = Buffer.data();
+ if (j == e)
+ break;
}
- return 0;
+ return hasError ? 1 : 0;
}