//===----------------------------------------------------------------------===//
#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.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/StringMap.h"
#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
using namespace llvm;
static cl::opt<std::string>
/// used for trailing CHECK-NOTs.
bool MatchEOF;
+ /// MatchNot
+ bool MatchNot;
+
+ /// MatchDag
+ bool MatchDag;
+
/// FixedStr - If non-empty, this pattern is a fixed string match with the
/// specified fixed string.
StringRef FixedStr;
/// RegEx - If non-empty, this is a regex pattern.
std::string RegExStr;
+ /// \brief Contains the number of line this pattern is in.
+ unsigned LineNumber;
+
/// VariableUses - Entries in this vector map to uses of a variable in the
/// pattern, e.g. "foo[[bar]]baz". In this case, the RegExStr will contain
/// "foobaz" and we'll get an entry in this vector that tells us to insert the
/// value of bar at offset 3.
std::vector<std::pair<StringRef, unsigned> > VariableUses;
- /// VariableDefs - Entries in this vector map to definitions of a variable in
- /// the pattern, e.g. "foo[[bar:.*]]baz". In this case, the RegExStr will
- /// contain "foo(.*)baz" and VariableDefs will contain the pair "bar",1. The
- /// index indicates what parenthesized value captures the variable value.
- std::vector<std::pair<StringRef, unsigned> > VariableDefs;
+ /// VariableDefs - Maps definitions of variables to their parenthesized
+ /// capture numbers.
+ /// E.g. for the pattern "foo[[bar:.*]]baz", VariableDefs will map "bar" to 1.
+ std::map<StringRef, unsigned> VariableDefs;
public:
- Pattern(bool matchEOF = false) : MatchEOF(matchEOF) { }
+ Pattern(bool matchEOF = false)
+ : MatchEOF(matchEOF), MatchNot(false), MatchDag(false) { }
+
+ /// getLoc - Return the location in source code.
+ SMLoc getLoc() const { return PatternLoc; }
- bool ParsePattern(StringRef PatternStr, SourceMgr &SM);
+ /// 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);
/// 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()); }
+
+ void setMatchNot(bool Not) { MatchNot = Not; }
+ bool getMatchNot() const { return MatchNot; }
+
+ void setMatchDag(bool Dag) { MatchDag = Dag; }
+ bool getMatchDag() const { return MatchDag; }
+
private:
static void AddFixedStringToRegEx(StringRef FixedStr, std::string &TheStr);
- bool AddRegExToRegEx(StringRef RegExStr, unsigned &CurParen, SourceMgr &SM);
+ bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM);
+ void AddBackrefToRegEx(unsigned BackrefNum);
/// ComputeMatchDistance - Compute an arbitrary estimate for the quality of
/// matching this pattern at the start of \arg Buffer; a distance of zero
/// should correspond to a perfect match.
unsigned ComputeMatchDistance(StringRef Buffer,
const StringMap<StringRef> &VariableTable) const;
+
+ /// \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);
};
-bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM) {
+bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM,
+ unsigned LineNumber) {
+ this->LineNumber = LineNumber;
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
// Ignore trailing whitespace.
while (!PatternStr.empty()) {
// RegEx matches.
if (PatternStr.startswith("{{")) {
-
- // Otherwise, this is the start of a regex match. Scan for the }}.
+ // This is the start of a regex match. Scan for the }}.
size_t End = PatternStr.find("}}");
if (End == StringRef::npos) {
SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
// 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));
+
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(':');
return true;
}
- // Verify that the name is well formed.
- for (unsigned i = 0, e = Name.size(); i != e; ++i)
- if (Name[i] != '_' && !isalnum(Name[i])) {
+ // Verify that the name/expression is well formed. FileCheck currently
+ // supports @LINE, @LINE+number, @LINE-number expressions. The check here
+ // is relaxed, more strict check is performed in \c EvaluateExpression.
+ bool IsExpression = false;
+ for (unsigned i = 0, e = Name.size(); i != e; ++i) {
+ if (i == 0 && Name[i] == '@') {
+ if (NameEnd != StringRef::npos) {
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
+ SourceMgr::DK_Error,
+ "invalid name in named regex definition");
+ return true;
+ }
+ IsExpression = true;
+ continue;
+ }
+ if (Name[i] != '_' && !isalnum(Name[i]) &&
+ (!IsExpression || (Name[i] != '+' && Name[i] != '-'))) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()+i),
SourceMgr::DK_Error, "invalid name in named regex");
return true;
}
+ }
// 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;
// Handle [[foo]].
if (NameEnd == StringRef::npos) {
- VariableUses.push_back(std::make_pair(Name, RegExStr.size()));
+ // Handle variables that were defined earlier on the same line by
+ // emitting a backreference.
+ if (VariableDefs.find(Name) != VariableDefs.end()) {
+ unsigned VarParenNum = VariableDefs[Name];
+ if (VarParenNum < 1 || VarParenNum > 9) {
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
+ SourceMgr::DK_Error,
+ "Can't back-reference more than 9 variables");
+ return true;
+ }
+ AddBackrefToRegEx(VarParenNum);
+ } else {
+ VariableUses.push_back(std::make_pair(Name, RegExStr.size()));
+ }
continue;
}
// Handle [[foo:.*]].
- VariableDefs.push_back(std::make_pair(Name, CurParen));
+ VariableDefs[Name] = CurParen;
RegExStr += '(';
++CurParen;
FixedMatchEnd = std::min(FixedMatchEnd, PatternStr.find("[["));
AddFixedStringToRegEx(PatternStr.substr(0, FixedMatchEnd), RegExStr);
PatternStr = PatternStr.substr(FixedMatchEnd);
- continue;
}
return false;
}
}
-bool Pattern::AddRegExToRegEx(StringRef RegexStr, unsigned &CurParen,
+bool Pattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen,
SourceMgr &SM) {
- Regex R(RegexStr);
+ Regex R(RS);
std::string Error;
if (!R.isValid(Error)) {
- SM.PrintMessage(SMLoc::getFromPointer(RegexStr.data()), SourceMgr::DK_Error,
+ SM.PrintMessage(SMLoc::getFromPointer(RS.data()), SourceMgr::DK_Error,
"invalid regex: " + Error);
return true;
}
- RegExStr += RegexStr.str();
+ RegExStr += RS.str();
CurParen += R.getNumMatches();
return false;
}
+void Pattern::AddBackrefToRegEx(unsigned BackrefNum) {
+ assert(BackrefNum >= 1 && BackrefNum <= 9 && "Invalid backref number");
+ std::string Backref = std::string("\\") +
+ std::string(1, '0' + BackrefNum);
+ RegExStr += Backref;
+}
+
+bool Pattern::EvaluateExpression(StringRef Expr, std::string &Value) const {
+ // The only supported expression is @LINE([\+-]\d+)?
+ if (!Expr.startswith("@LINE"))
+ return false;
+ Expr = Expr.substr(StringRef("@LINE").size());
+ int Offset = 0;
+ if (!Expr.empty()) {
+ if (Expr[0] == '+')
+ Expr = Expr.substr(1);
+ else if (Expr[0] != '-')
+ return false;
+ if (Expr.getAsInteger(10, Offset))
+ return false;
+ }
+ Value = llvm::itostr(LineNumber + Offset);
+ return true;
+}
+
/// 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
/// there is a match, the size of the matched string is returned in MatchLen.
unsigned InsertOffset = 0;
for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) {
- StringMap<StringRef>::iterator it =
- VariableTable.find(VariableUses[i].first);
- // If the variable is undefined, return an error.
- if (it == VariableTable.end())
- return StringRef::npos;
-
- // Look up the value and escape it so that we can plop it into the regex.
std::string Value;
- AddFixedStringToRegEx(it->second, Value);
+
+ if (VariableUses[i].first[0] == '@') {
+ if (!EvaluateExpression(VariableUses[i].first, Value))
+ return StringRef::npos;
+ } else {
+ StringMap<StringRef>::iterator it =
+ VariableTable.find(VariableUses[i].first);
+ // If the variable is undefined, return an error.
+ 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);
+ }
// Plop it into the regex at the adjusted offset.
TmpStr.insert(TmpStr.begin()+VariableUses[i].second+InsertOffset,
StringRef FullMatch = MatchInfo[0];
// If this defines any variables, remember their values.
- for (unsigned i = 0, e = VariableDefs.size(); i != e; ++i) {
- assert(VariableDefs[i].second < MatchInfo.size() &&
- "Internal paren error");
- VariableTable[VariableDefs[i].first] = MatchInfo[VariableDefs[i].second];
+ for (std::map<StringRef, unsigned>::const_iterator I = VariableDefs.begin(),
+ E = VariableDefs.end();
+ I != E; ++I) {
+ assert(I->second < MatchInfo.size() && "Internal paren error");
+ VariableTable[I->first] = MatchInfo[I->second];
}
MatchLen = FullMatch.size();
// variable values.
if (!VariableUses.empty()) {
for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) {
- StringRef Var = VariableUses[i].first;
- StringMap<StringRef>::const_iterator it = VariableTable.find(Var);
SmallString<256> Msg;
raw_svector_ostream OS(Msg);
-
- // Check for undefined variable references.
- if (it == VariableTable.end()) {
- OS << "uses undefined variable \"";
- OS.write_escaped(Var) << "\"";;
+ StringRef Var = VariableUses[i].first;
+ if (Var[0] == '@') {
+ std::string Value;
+ if (EvaluateExpression(Var, Value)) {
+ OS << "with expression \"";
+ OS.write_escaped(Var) << "\" equal to \"";
+ OS.write_escaped(Value) << "\"";
+ } else {
+ OS << "uses incorrect expression \"";
+ OS.write_escaped(Var) << "\"";
+ }
} else {
- OS << "with variable \"";
- OS.write_escaped(Var) << "\" equal to \"";
- OS.write_escaped(it->second) << "\"";
+ StringMap<StringRef>::const_iterator it = VariableTable.find(Var);
+
+ // Check for undefined variable references.
+ if (it == VariableTable.end()) {
+ OS << "uses undefined variable \"";
+ OS.write_escaped(Var) << "\"";
+ } else {
+ OS << "with variable \"";
+ OS.write_escaped(Var) << "\" equal to \"";
+ OS.write_escaped(it->second) << "\"";
+ }
}
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
}
}
+size_t Pattern::FindRegexVarEnd(StringRef Str) {
+ // 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 ']':
+ assert(BracketDepth > 0 && "Invalid regex");
+ BracketDepth--;
+ break;
+ }
+ Str = Str.substr(1);
+ Offset++;
+ }
+ }
+
+ return StringRef::npos;
+}
+
+
//===----------------------------------------------------------------------===//
// Check Strings.
//===----------------------------------------------------------------------===//
/// to a CHECK: directive.
bool IsCheckNext;
- /// NotStrings - These are all of the strings that are disallowed from
+ /// IsCheckLabel - This is true if this is a CHECK-LABEL: directive (as
+ /// opposed to a CHECK: directive.
+ bool IsCheckLabel;
+
+ /// 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, SMLoc L, bool isCheckNext, bool isCheckLabel)
+ : Pat(P), Loc(L), IsCheckNext(isCheckNext), IsCheckLabel(isCheckLabel) {}
+
+ /// Check - Match check string and its "not strings" and/or "dag strings".
+ size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabel,
+ 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;
- CheckString(const Pattern &P, SMLoc L, bool isCheckNext)
- : Pat(P), Loc(L), IsCheckNext(isCheckNext) {}
+ /// 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;
+
+ /// 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 MemoryBuffer *CanonicalizeInputFile(MemoryBuffer *MB,
+ bool PreserveHorizontal) {
SmallString<128> NewFile;
NewFile.reserve(MB->getBufferSize());
continue;
}
- // If C is not a horizontal whitespace, skip it.
- 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;
}
/// 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) {
- // Open the check file, and tell SourceMgr about it.
OwningPtr<MemoryBuffer> File;
if (error_code ec =
- MemoryBuffer::getFileOrSTDIN(CheckFilename.c_str(), File)) {
+ MemoryBuffer::getFileOrSTDIN(CheckFilename, File)) {
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);
+ // buffer containing the CHECK lines. Remove DOS style line endings.
+ MemoryBuffer *F =
+ CanonicalizeInputFile(File.take(), NoCanonicalizeWhiteSpace);
SM.AddNewSourceBuffer(F, SMLoc());
// Find all instances of CheckPrefix followed by : in the file.
StringRef Buffer = F->getBuffer();
+ std::vector<Pattern> DagNotMatches;
- std::vector<std::pair<SMLoc, Pattern> > NotMatches;
+ // 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.
- Buffer = Buffer.substr(Buffer.find(CheckPrefix));
-
+ size_t PrefixLoc = Buffer.find(CheckPrefix);
// If we didn't find a match, we're done.
- if (Buffer.empty())
+ if (PrefixLoc == StringRef::npos)
break;
- const char *CheckPrefixStart = Buffer.data();
+ LineNumber += Buffer.substr(0, PrefixLoc).count('\n');
+
+ // Keep the charcter before our prefix so we can validate that we have
+ // found our prefix, and account for cases when PrefixLoc is 0.
+ Buffer = Buffer.substr(std::min(PrefixLoc-1, PrefixLoc));
+
+ const char *CheckPrefixStart = 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;
+ bool IsCheckNext = false, IsCheckNot = false, IsCheckDag = false,
+ IsCheckLabel = false;
+
+ // Make sure we have actually found our prefix, and not a word containing
+ // our prefix.
+ if (PrefixLoc != 0 && (isalnum(Buffer[0]) ||
+ Buffer[0] == '-' ||
+ Buffer[0] == '_')) {
+ Buffer = Buffer.substr(CheckPrefix.size());
+ continue;
+ }
// Verify that the : is present after the prefix.
if (Buffer[CheckPrefix.size()] == ':') {
memcmp(Buffer.data()+CheckPrefix.size(), "-NOT:", 5) == 0) {
Buffer = Buffer.substr(CheckPrefix.size()+5);
IsCheckNot = true;
+ } else if (Buffer.size() > CheckPrefix.size()+5 &&
+ memcmp(Buffer.data()+CheckPrefix.size(), "-DAG:", 5) == 0) {
+ Buffer = Buffer.substr(CheckPrefix.size()+5);
+ IsCheckDag = true;
+ } else if (Buffer.size() > CheckPrefix.size()+7 &&
+ memcmp(Buffer.data()+CheckPrefix.size(), "-LABEL:", 7) == 0) {
+ Buffer = Buffer.substr(CheckPrefix.size()+7);
+ IsCheckLabel = true;
} else {
Buffer = Buffer.substr(1);
continue;
// Parse the pattern.
Pattern P;
- if (P.ParsePattern(Buffer.substr(0, EOL), SM))
+ if (P.ParsePattern(Buffer.substr(0, EOL), SM, LineNumber))
return true;
- Buffer = Buffer.substr(EOL);
+ // Verify that CHECK-LABEL lines do not define or use variables
+ if (IsCheckLabel && P.hasVariable()) {
+ SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart),
+ SourceMgr::DK_Error,
+ "found '"+CheckPrefix+"-LABEL:' with variable definition"
+ " or use'");
+ return true;
+ }
+ P.setMatchNot(IsCheckNot);
+ P.setMatchDag(IsCheckDag);
+
+ Buffer = Buffer.substr(EOL);
// Verify that CHECK-NEXT lines have at least one CHECK line before them.
if (IsCheckNext && CheckStrings.empty()) {
return true;
}
- // Handle CHECK-NOT.
- if (IsCheckNot) {
- NotMatches.push_back(std::make_pair(SMLoc::getFromPointer(Buffer.data()),
- P));
+ // Handle CHECK-DAG/-NOT.
+ if (IsCheckDag || IsCheckNot) {
+ DagNotMatches.push_back(P);
continue;
}
-
// Okay, add the string we captured to the output vector and move on.
CheckStrings.push_back(CheckString(P,
PatternLoc,
- IsCheckNext));
- std::swap(NotMatches, CheckStrings.back().NotStrings);
+ IsCheckNext,
+ IsCheckLabel));
+ std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
}
- // Add an EOF pattern for any trailing CHECK-NOTs.
- if (!NotMatches.empty()) {
+ // Add an EOF pattern for any trailing CHECK-DAG/-NOTs.
+ if (!DagNotMatches.empty()) {
CheckStrings.push_back(CheckString(Pattern(true),
SMLoc::getFromPointer(Buffer.data()),
+ false,
false));
- std::swap(NotMatches, CheckStrings.back().NotStrings);
+ std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
}
if (CheckStrings.empty()) {
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
}
}
+size_t CheckString::Check(const SourceMgr &SM, StringRef Buffer,
+ bool IsLabel, size_t &MatchLen,
+ StringMap<StringRef> &VariableTable) const {
+ size_t LastPos = 0;
+ std::vector<const Pattern *> NotStrings;
+
+ if (!IsLabel) {
+ // 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;
+
+ if (!IsLabel) {
+ 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 (!IsCheckNext)
+ 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");
+
+ unsigned NumNewLines = CountNumNewlinesBetween(Buffer);
+
+ if (NumNewLines == 0) {
+ SM.PrintMessage(Loc, SourceMgr::DK_Error, CheckPrefix+
+ "-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, CheckPrefix+
+ "-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");
+ 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->getMatchNot() && "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,
+ CheckPrefix+"-NOT: string occurred!");
+ SM.PrintMessage(Pat->getLoc(), SourceMgr::DK_Note,
+ CheckPrefix+"-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.getMatchDag() ^ Pat.getMatchNot()) &&
+ "Invalid CHECK-DAG or CHECK-NOT!");
+
+ if (Pat.getMatchNot()) {
+ NotStrings.push_back(&Pat);
+ continue;
+ }
+
+ assert(Pat.getMatchDag() && "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,
+ CheckPrefix+"-DAG: found a match of CHECK-DAG"
+ " reordering across a CHECK-NOT");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + LastPos),
+ SourceMgr::DK_Note,
+ CheckPrefix+"-DAG: the farthest match of CHECK-DAG"
+ " is found here");
+ SM.PrintMessage(NotStrings[0]->getLoc(), SourceMgr::DK_Note,
+ CheckPrefix+"-NOT: the crossed pattern specified"
+ " here");
+ SM.PrintMessage(Pat.getLoc(), SourceMgr::DK_Note,
+ CheckPrefix+"-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;
+}
+
+bool ValidateCheckPrefix() {
+ // The check prefix must contain only alphanumeric, hyphens and underscores.
+ Regex prefixValidator("^[a-zA-Z0-9_-]*$");
+ return prefixValidator.match(CheckPrefix);
+}
+
int main(int argc, char **argv) {
sys::PrintStackTraceOnErrorSignal();
PrettyStackTraceProgram X(argc, argv);
cl::ParseCommandLineOptions(argc, argv);
+ if (!ValidateCheckPrefix()) {
+ errs() << "Supplied check-prefix is invalid! Prefixes must start with a "
+ "letter and contain only alphanumeric characters, hyphens and "
+ "underscores\n";
+ return 2;
+ }
+
SourceMgr SM;
// Read the expected strings from the check file.
// Open the file to check and add it to SourceMgr.
OwningPtr<MemoryBuffer> File;
if (error_code ec =
- MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), File)) {
+ MemoryBuffer::getFileOrSTDIN(InputFilename, File)) {
errs() << "Could not open input file '" << InputFilename << "': "
<< ec.message() << '\n';
- return true;
+ return 2;
}
- MemoryBuffer *F = File.take();
- if (F->getBufferSize() == 0) {
+ if (File->getBufferSize() == 0) {
errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
- return 1;
+ return 2;
}
-
+
// Remove duplicate spaces in the input file if requested.
- if (!NoCanonicalizeWhiteSpace)
- F = CanonicalizeInputFile(F);
+ // Remove DOS style line endings.
+ MemoryBuffer *F =
+ CanonicalizeInputFile(File.take(), NoCanonicalizeWhiteSpace);
SM.AddNewSourceBuffer(F, SMLoc());
// file.
StringRef Buffer = F->getBuffer();
- const char *LastMatch = Buffer.data();
+ bool hasError = false;
- for (unsigned StrNo = 0, e = CheckStrings.size(); StrNo != e; ++StrNo) {
- const CheckString &CheckStr = CheckStrings[StrNo];
+ unsigned i = 0, j = 0, e = CheckStrings.size();
- StringRef SearchFrom = Buffer;
+ while (true) {
+ StringRef CheckRegion;
+ if (j == e) {
+ CheckRegion = Buffer;
+ } else {
+ const CheckString &CheckLabelStr = CheckStrings[j];
+ if (!CheckLabelStr.IsCheckLabel) {
+ ++j;
+ continue;
+ }
- // Find StrNo in the file.
- size_t MatchLen = 0;
- size_t MatchPos = CheckStr.Pat.Match(Buffer, MatchLen, VariableTable);
- Buffer = Buffer.substr(MatchPos);
+ // 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;
+ }
- // If we didn't find a match, reject the input.
- if (MatchPos == StringRef::npos) {
- PrintCheckFailed(SM, CheckStr, SearchFrom, VariableTable);
- return 1;
+ CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen);
+ Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen);
+ ++j;
}
- StringRef SkippedRegion(LastMatch, Buffer.data()-LastMatch);
+ for ( ; i != j; ++i) {
+ const CheckString &CheckStr = CheckStrings[i];
- // 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;
- }
+ // 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 MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen,
+ VariableTable);
- 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;
+ if (MatchPos == StringRef::npos) {
+ hasError = true;
+ i = j;
+ break;
}
- }
- // 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) {
- 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;
+ 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;
}