#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>
using namespace llvm;
/// 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
Pattern(bool matchEOF = false) : MatchEOF(matchEOF) { }
- 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
/// 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;
};
-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.
// Check that there is something on the line.
if (PatternStr.empty()) {
- SM.PrintMessage(PatternLoc, "found empty check string with prefix '" +
- CheckPrefix+":'", "error");
+ SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
+ "found empty check string with prefix '" +
+ CheckPrefix+":'");
return true;
}
}
// Paren value #0 is for the fully matched string. Any new parenthesized
- // values add from their.
+ // values add from there.
unsigned CurParen = 1;
// Otherwise, there is at least one regex piece. Build up the regex pattern
// by escaping scary characters in fixed strings, building up one big regex.
while (!PatternStr.empty()) {
// RegEx matches.
- if (PatternStr.size() >= 2 &&
- PatternStr[0] == '{' && PatternStr[1] == '{') {
-
- // Otherwise, this is the start of a regex match. Scan for the }}.
+ if (PatternStr.startswith("{{")) {
+ // 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()),
- "found start of regex string with no end '}}'", "error");
+ SourceMgr::DK_Error,
+ "found start of regex string with no end '}}'");
return true;
}
+ // Enclose {{}} patterns in parens just like [[]] even though we're not
+ // capturing the result for any purpose. This is required in case the
+ // expression contains an alternation like: CHECK: abc{{x|z}}def. We
+ // want this to turn into: "abc(x|z)def" not "abcx|zdef".
+ RegExStr += '(';
+ ++CurParen;
+
if (AddRegExToRegEx(PatternStr.substr(2, End-2), CurParen, SM))
return true;
+ RegExStr += ')';
+
PatternStr = PatternStr.substr(End+2);
continue;
}
// second form is [[foo]] which is a reference to foo. The variable name
// 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.size() >= 2 &&
- PatternStr[0] == '[' && PatternStr[1] == '[') {
+ if (PatternStr.startswith("[[")) {
// Verify that it is terminated properly.
size_t End = PatternStr.find("]]");
if (End == StringRef::npos) {
SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
- "invalid named regex reference, no ]] found", "error");
+ SourceMgr::DK_Error,
+ "invalid named regex reference, no ]] found");
return true;
}
StringRef Name = MatchStr.substr(0, NameEnd);
if (Name.empty()) {
- SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
- "invalid name in named regex: empty name", "error");
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
+ "invalid name in named regex: empty name");
return true;
}
- // Verify that the name is well formed.
- for (unsigned i = 0, e = Name.size(); i != e; ++i)
- if (Name[i] != '_' &&
- (Name[i] < 'a' || Name[i] > 'z') &&
- (Name[i] < 'A' || Name[i] > 'Z') &&
- (Name[i] < '0' || Name[i] > '9')) {
+ // 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),
- "invalid name in named regex", "error");
+ SourceMgr::DK_Error, "invalid name in named regex");
return true;
}
+ }
// Name can't start with a digit.
if (isdigit(Name[0])) {
- SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
- "invalid name in named regex", "error");
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
+ "invalid name in named regex");
return true;
}
FixedMatchEnd = std::min(FixedMatchEnd, PatternStr.find("[["));
AddFixedStringToRegEx(PatternStr.substr(0, FixedMatchEnd), RegExStr);
PatternStr = PatternStr.substr(FixedMatchEnd);
- continue;
}
return false;
Regex R(RegexStr);
std::string Error;
if (!R.isValid(Error)) {
- SM.PrintMessage(SMLoc::getFromPointer(RegexStr.data()),
- "invalid regex: " + Error, "error");
+ SM.PrintMessage(SMLoc::getFromPointer(RegexStr.data()), SourceMgr::DK_Error,
+ "invalid regex: " + Error);
return true;
}
return false;
}
+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,
// 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()), OS.str(), "note",
- /*ShowLine=*/false);
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
+ OS.str());
}
}
// line.
if (Best && Best != StringRef::npos && BestQuality < 50) {
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + Best),
- "possible intended match here", "note");
+ SourceMgr::DK_Note, "possible intended match here");
// FIXME: If we wanted to be really friendly we would show why the match
// failed, as it can be hard to spot simple one character differences.
continue;
}
- // If C is not a horizontal whitespace, skip it.
+ // If current char is not a horizontal whitespace, dump it to output as is.
if (*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)) {
// Find all instances of CheckPrefix followed by : in the file.
StringRef Buffer = F->getBuffer();
-
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;
+ LineNumber += Buffer.substr(0, PrefixLoc).count('\n');
+
+ Buffer = Buffer.substr(PrefixLoc);
+
const char *CheckPrefixStart = Buffer.data();
// When we find a check prefix, keep track of whether we find CHECK: or
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()+7);
+ 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()+6);
+ Buffer = Buffer.substr(CheckPrefix.size()+5);
IsCheckNot = true;
} else {
Buffer = Buffer.substr(1);
// 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-NEXT lines have at least one CHECK line before them.
if (IsCheckNext && CheckStrings.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(CheckPrefixStart),
+ SourceMgr::DK_Error,
"found '"+CheckPrefix+"-NEXT:' without previous '"+
- CheckPrefix+ ": line", "error");
+ CheckPrefix+ ": line");
return true;
}
continue;
}
-
// Okay, add the string we captured to the output vector and move on.
CheckStrings.push_back(CheckString(P,
PatternLoc,
StringRef Buffer,
StringMap<StringRef> &VariableTable) {
// Otherwise, we have an error, emit an error message.
- SM.PrintMessage(CheckStr.Loc, "expected string not found in input",
- "error");
+ SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error,
+ "expected string not found in input");
// Print the "scanning from here" line. If the current position is at the
// end of a line, advance to the start of the next line.
Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r"));
- SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), "scanning from here",
- "note");
+ SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
+ "scanning from here");
// Allow the pattern to print additional information if desired.
CheckStr.Pat.PrintFailureInfo(SM, Buffer, VariableTable);
MemoryBuffer::getFileOrSTDIN(InputFilename.c_str(), File)) {
errs() << "Could not open input file '" << InputFilename << "': "
<< ec.message() << '\n';
- return true;
+ return 2;
}
MemoryBuffer *F = File.take();
if (F->getBufferSize() == 0) {
errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
- return 1;
+ return 2;
}
// Remove duplicate spaces in the input file if requested.
unsigned NumNewLines = CountNumNewlinesBetween(SkippedRegion);
if (NumNewLines == 0) {
- SM.PrintMessage(CheckStr.Loc,
- CheckPrefix+"-NEXT: is on the same line as previous match",
- "error");
+ SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error,
+ CheckPrefix+"-NEXT: is on the same line as previous match");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
- "'next' match was here", "note");
- SM.PrintMessage(SMLoc::getFromPointer(LastMatch),
- "previous match was here", "note");
+ SourceMgr::DK_Note, "'next' match was here");
+ SM.PrintMessage(SMLoc::getFromPointer(LastMatch), SourceMgr::DK_Note,
+ "previous match was here");
return 1;
}
if (NumNewLines != 1) {
- SM.PrintMessage(CheckStr.Loc,
- CheckPrefix+
- "-NEXT: is not on the line after the previous match",
- "error");
+ SM.PrintMessage(CheckStr.Loc, SourceMgr::DK_Error, CheckPrefix+
+ "-NEXT: is not on the line after the previous match");
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
- "'next' match was here", "note");
- SM.PrintMessage(SMLoc::getFromPointer(LastMatch),
- "previous match was here", "note");
+ SourceMgr::DK_Note, "'next' match was here");
+ SM.PrintMessage(SMLoc::getFromPointer(LastMatch), SourceMgr::DK_Note,
+ "previous match was here");
return 1;
}
}
VariableTable);
if (Pos == StringRef::npos) continue;
- SM.PrintMessage(SMLoc::getFromPointer(LastMatch+Pos),
- CheckPrefix+"-NOT: string occurred!", "error");
- SM.PrintMessage(CheckStr.NotStrings[ChunkNo].first,
- CheckPrefix+"-NOT: pattern specified here", "note");
+ 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;
}