mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-06-20 10:24:12 +00:00
Support for [[@LINE]], [[@LINE+<offset>]], [[@LINE-<offset>]] expressions in
FileCheck. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@167978 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@ -252,3 +252,30 @@ advantage of the fact that FileCheck is not actually line-oriented when it
|
|||||||
matches, this allows you to define two separate "``CHECK``" lines that match on
|
matches, this allows you to define two separate "``CHECK``" lines that match on
|
||||||
the same line.
|
the same line.
|
||||||
|
|
||||||
|
|
||||||
|
FileCheck Expressions
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
||||||
|
Sometimes there's a need to verify output which refers line numbers of the match
|
||||||
|
file, e.g. when testing compiler diagnostics. This introduces a certain
|
||||||
|
fragility of the match file structure, as CHECK: lines contain absolute line
|
||||||
|
numbers in the same file, which have to be updated whenever line numbers change
|
||||||
|
due to text addition or deletion.
|
||||||
|
|
||||||
|
To support this case, FileCheck allows using ``[[@LINE]]``,
|
||||||
|
``[[@LINE+<offset>]]``, ``[[@LINE-<offset>]]`` expressions in patterns. These
|
||||||
|
expressions expand to a number of the line where a pattern is located (with an
|
||||||
|
optional integer offset).
|
||||||
|
|
||||||
|
This way match patterns can be put near the relevant test lines and include
|
||||||
|
relative line number references, for example:
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
// CHECK: test.cpp:[[@LINE+4]]:6: error: expected ';' after top level declarator
|
||||||
|
// CHECK-NEXT: {{^int a}}
|
||||||
|
// CHECK-NEXT: {{^ \^}}
|
||||||
|
// CHECK-NEXT: {{^ ;}}
|
||||||
|
int a
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "llvm/Support/Signals.h"
|
#include "llvm/Support/Signals.h"
|
||||||
#include "llvm/Support/system_error.h"
|
#include "llvm/Support/system_error.h"
|
||||||
#include "llvm/ADT/SmallString.h"
|
#include "llvm/ADT/SmallString.h"
|
||||||
|
#include "llvm/ADT/StringExtras.h"
|
||||||
#include "llvm/ADT/StringMap.h"
|
#include "llvm/ADT/StringMap.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
@ -63,6 +64,9 @@ class Pattern {
|
|||||||
/// RegEx - If non-empty, this is a regex pattern.
|
/// RegEx - If non-empty, this is a regex pattern.
|
||||||
std::string RegExStr;
|
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
|
/// 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
|
/// 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
|
/// "foobaz" and we'll get an entry in this vector that tells us to insert the
|
||||||
@ -79,7 +83,7 @@ public:
|
|||||||
|
|
||||||
Pattern(bool matchEOF = false) : MatchEOF(matchEOF) { }
|
Pattern(bool matchEOF = false) : MatchEOF(matchEOF) { }
|
||||||
|
|
||||||
bool ParsePattern(StringRef PatternStr, SourceMgr &SM);
|
bool ParsePattern(StringRef PatternStr, SourceMgr &SM, unsigned LineNumber);
|
||||||
|
|
||||||
/// Match - Match the pattern string against the input buffer Buffer. This
|
/// 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
|
/// returns the position that is matched or npos if there is no match. If
|
||||||
@ -104,10 +108,16 @@ private:
|
|||||||
/// should correspond to a perfect match.
|
/// should correspond to a perfect match.
|
||||||
unsigned ComputeMatchDistance(StringRef Buffer,
|
unsigned ComputeMatchDistance(StringRef Buffer,
|
||||||
const StringMap<StringRef> &VariableTable) const;
|
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());
|
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
|
||||||
|
|
||||||
// Ignore trailing whitespace.
|
// Ignore trailing whitespace.
|
||||||
@ -193,13 +203,28 @@ bool Pattern::ParsePattern(StringRef PatternStr, SourceMgr &SM) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the name is well formed.
|
// Verify that the name/expression is well formed. FileCheck currently
|
||||||
for (unsigned i = 0, e = Name.size(); i != e; ++i)
|
// supports @LINE, @LINE+number, @LINE-number expressions. The check here
|
||||||
if (Name[i] != '_' && !isalnum(Name[i])) {
|
// 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),
|
SM.PrintMessage(SMLoc::getFromPointer(Name.data()+i),
|
||||||
SourceMgr::DK_Error, "invalid name in named regex");
|
SourceMgr::DK_Error, "invalid name in named regex");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Name can't start with a digit.
|
// Name can't start with a digit.
|
||||||
if (isdigit(Name[0])) {
|
if (isdigit(Name[0])) {
|
||||||
@ -279,6 +304,24 @@ bool Pattern::AddRegExToRegEx(StringRef RegexStr, unsigned &CurParen,
|
|||||||
return false;
|
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
|
/// 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
|
/// 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.
|
/// there is a match, the size of the matched string is returned in MatchLen.
|
||||||
@ -307,6 +350,12 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen,
|
|||||||
|
|
||||||
unsigned InsertOffset = 0;
|
unsigned InsertOffset = 0;
|
||||||
for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) {
|
for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) {
|
||||||
|
std::string Value;
|
||||||
|
|
||||||
|
if (VariableUses[i].first[0] == '@') {
|
||||||
|
if (!EvaluateExpression(VariableUses[i].first, Value))
|
||||||
|
return StringRef::npos;
|
||||||
|
} else {
|
||||||
StringMap<StringRef>::iterator it =
|
StringMap<StringRef>::iterator it =
|
||||||
VariableTable.find(VariableUses[i].first);
|
VariableTable.find(VariableUses[i].first);
|
||||||
// If the variable is undefined, return an error.
|
// If the variable is undefined, return an error.
|
||||||
@ -314,8 +363,8 @@ size_t Pattern::Match(StringRef Buffer, size_t &MatchLen,
|
|||||||
return StringRef::npos;
|
return StringRef::npos;
|
||||||
|
|
||||||
// Look up the value and escape it so that we can plop it into the regex.
|
// Look up the value and escape it so that we can plop it into the regex.
|
||||||
std::string Value;
|
|
||||||
AddFixedStringToRegEx(it->second, Value);
|
AddFixedStringToRegEx(it->second, Value);
|
||||||
|
}
|
||||||
|
|
||||||
// Plop it into the regex at the adjusted offset.
|
// Plop it into the regex at the adjusted offset.
|
||||||
TmpStr.insert(TmpStr.begin()+VariableUses[i].second+InsertOffset,
|
TmpStr.insert(TmpStr.begin()+VariableUses[i].second+InsertOffset,
|
||||||
@ -371,20 +420,32 @@ void Pattern::PrintFailureInfo(const SourceMgr &SM, StringRef Buffer,
|
|||||||
// variable values.
|
// variable values.
|
||||||
if (!VariableUses.empty()) {
|
if (!VariableUses.empty()) {
|
||||||
for (unsigned i = 0, e = VariableUses.size(); i != e; ++i) {
|
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;
|
SmallString<256> Msg;
|
||||||
raw_svector_ostream OS(Msg);
|
raw_svector_ostream OS(Msg);
|
||||||
|
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 {
|
||||||
|
StringMap<StringRef>::const_iterator it = VariableTable.find(Var);
|
||||||
|
|
||||||
// Check for undefined variable references.
|
// Check for undefined variable references.
|
||||||
if (it == VariableTable.end()) {
|
if (it == VariableTable.end()) {
|
||||||
OS << "uses undefined variable \"";
|
OS << "uses undefined variable \"";
|
||||||
OS.write_escaped(Var) << "\"";;
|
OS.write_escaped(Var) << "\"";
|
||||||
} else {
|
} else {
|
||||||
OS << "with variable \"";
|
OS << "with variable \"";
|
||||||
OS.write_escaped(Var) << "\" equal to \"";
|
OS.write_escaped(Var) << "\" equal to \"";
|
||||||
OS.write_escaped(it->second) << "\"";
|
OS.write_escaped(it->second) << "\"";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
|
SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
|
||||||
OS.str());
|
OS.str());
|
||||||
@ -518,14 +579,20 @@ static bool ReadCheckFile(SourceMgr &SM,
|
|||||||
|
|
||||||
std::vector<std::pair<SMLoc, Pattern> > NotMatches;
|
std::vector<std::pair<SMLoc, Pattern> > NotMatches;
|
||||||
|
|
||||||
|
unsigned LineNumber = 1;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
// See if Prefix occurs in the memory buffer.
|
// 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 we didn't find a match, we're done.
|
||||||
if (Buffer.empty())
|
if (PrefixLoc == StringRef::npos)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Recalculate line number.
|
||||||
|
LineNumber += Buffer.substr(0, PrefixLoc).count('\n');
|
||||||
|
|
||||||
|
Buffer = Buffer.substr(PrefixLoc);
|
||||||
|
|
||||||
const char *CheckPrefixStart = Buffer.data();
|
const char *CheckPrefixStart = Buffer.data();
|
||||||
|
|
||||||
// When we find a check prefix, keep track of whether we find CHECK: or
|
// When we find a check prefix, keep track of whether we find CHECK: or
|
||||||
@ -560,7 +627,7 @@ static bool ReadCheckFile(SourceMgr &SM,
|
|||||||
|
|
||||||
// Parse the pattern.
|
// Parse the pattern.
|
||||||
Pattern P;
|
Pattern P;
|
||||||
if (P.ParsePattern(Buffer.substr(0, EOL), SM))
|
if (P.ParsePattern(Buffer.substr(0, EOL), SM, LineNumber))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Buffer = Buffer.substr(EOL);
|
Buffer = Buffer.substr(EOL);
|
||||||
|
Reference in New Issue
Block a user