forked from OSchip/llvm-project
FileCheck [3/12]: Stricter parsing of @LINE expressions
Summary: This patch is part of a patch series to add support for FileCheck numeric expressions. This specific patch gives earlier and better diagnostics for the @LINE expressions. Rather than detect parsing errors at matching time, this commit adds enhance parsing to detect issues with @LINE expressions at parse time and diagnose them more accurately. Copyright: - Linaro (changes up to diff 183612 of revision D55940) - GraphCore (changes in later versions of revision D55940 and in new revision created off D55940) Reviewers: jhenderson, chandlerc, jdenny, probinson, grimar, arichardson, rnk Subscribers: hiraditya, llvm-commits, probinson, dblaikie, grimar, arichardson, tra, rnk, kristina, hfinkel, rogfer01, JonChesterfield Tags: #llvm Differential Revision: https://reviews.llvm.org/D60383 llvm-svn: 359475
This commit is contained in:
parent
41232d2661
commit
15cb1f1501
|
@ -155,6 +155,7 @@ public:
|
|||
/// Returns the pointer to the global state for all patterns in this
|
||||
/// FileCheck instance.
|
||||
FileCheckPatternContext *getContext() const { return Context; }
|
||||
|
||||
/// Return whether \p is a valid first character for a variable name.
|
||||
static bool isValidVarNameStart(char C);
|
||||
/// Verify that the string at the start of \p Str is a well formed variable.
|
||||
|
@ -162,6 +163,11 @@ public:
|
|||
/// variable and \p TrailIdx to the position of the last character that is
|
||||
/// part of the variable name. Otherwise, only return true.
|
||||
static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx);
|
||||
/// Parse a numeric expression involving pseudo variable \p Name with the
|
||||
/// string corresponding to the operation being performed in \p Trailer.
|
||||
/// Return whether parsing failed in which case errors are reported on \p SM.
|
||||
bool parseExpression(StringRef Name, StringRef Trailer,
|
||||
const SourceMgr &SM) const;
|
||||
bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM,
|
||||
unsigned LineNumber, const FileCheckRequest &Req);
|
||||
size_t match(StringRef Buffer, size_t &MatchLen) const;
|
||||
|
@ -184,7 +190,7 @@ private:
|
|||
bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM);
|
||||
void AddBackrefToRegEx(unsigned BackrefNum);
|
||||
unsigned computeMatchDistance(StringRef Buffer) const;
|
||||
bool EvaluateExpression(StringRef Expr, std::string &Value) const;
|
||||
void evaluateExpression(StringRef Expr, std::string &Value) const;
|
||||
size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,58 @@ bool FileCheckPattern::parseVariable(StringRef Str, bool &IsPseudo,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Parsing helper function that strips the first character in S and returns it.
|
||||
static char popFront(StringRef &S) {
|
||||
char C = S.front();
|
||||
S = S.drop_front();
|
||||
return C;
|
||||
}
|
||||
|
||||
bool FileCheckPattern::parseExpression(StringRef Name, StringRef Trailer,
|
||||
const SourceMgr &SM) const {
|
||||
if (!Name.equals("@LINE")) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"invalid pseudo variable '" + Name + "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if this is a supported operation and select function to perform it.
|
||||
if (Trailer.empty())
|
||||
return false;
|
||||
SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data());
|
||||
char Operator = popFront(Trailer);
|
||||
switch (Operator) {
|
||||
case '+':
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
|
||||
Twine("unsupported numeric operation '") + Twine(Operator) +
|
||||
"'");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse right operand.
|
||||
if (Trailer.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
|
||||
"missing operand in numeric expression '" + Trailer + "'");
|
||||
return true;
|
||||
}
|
||||
uint64_t Offset;
|
||||
if (Trailer.consumeInteger(10, Offset)) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
|
||||
"invalid offset in numeric expression '" + Trailer + "'");
|
||||
return true;
|
||||
}
|
||||
if (!Trailer.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
|
||||
"unexpected characters at end of numeric expression '" +
|
||||
Trailer + "'");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Parses the given string into the Pattern.
|
||||
///
|
||||
/// \p Prefix provides which prefix is being matched, \p SM provides the
|
||||
|
@ -163,6 +215,14 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
|
|||
MatchStr = MatchStr.substr(0, End);
|
||||
PatternStr = PatternStr.substr(End + 4);
|
||||
|
||||
size_t VarEndIdx = MatchStr.find(":");
|
||||
size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
|
||||
if (SpacePos != StringRef::npos) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
|
||||
SourceMgr::DK_Error, "unexpected whitespace");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the regex name (e.g. "foo") and verify it is well formed.
|
||||
bool IsPseudo;
|
||||
unsigned TrailIdx;
|
||||
|
@ -174,7 +234,7 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
|
|||
|
||||
StringRef Name = MatchStr.substr(0, TrailIdx);
|
||||
StringRef Trailer = MatchStr.substr(TrailIdx);
|
||||
bool IsVarDef = (Trailer.find(":") != StringRef::npos);
|
||||
bool IsVarDef = (VarEndIdx != StringRef::npos);
|
||||
|
||||
if (IsVarDef && (IsPseudo || !Trailer.consume_front(":"))) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
|
||||
|
@ -183,17 +243,9 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
|
|||
return true;
|
||||
}
|
||||
|
||||
// Verify that the name/expression is well formed. FileCheck currently
|
||||
// supports @LINE, @LINE+number, @LINE-number expressions. The check here
|
||||
// is relaxed. A stricter check is performed in \c EvaluateExpression.
|
||||
if (IsPseudo) {
|
||||
for (unsigned I = 0, E = Trailer.size(); I != E; ++I) {
|
||||
if (!isalnum(Trailer[I]) && Trailer[I] != '+' && Trailer[I] != '-') {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data() + I),
|
||||
SourceMgr::DK_Error, "invalid name in named regex");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!IsVarDef && IsPseudo) {
|
||||
if (parseExpression(Name, Trailer, SM))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle [[foo]].
|
||||
|
@ -264,24 +316,16 @@ void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) {
|
|||
}
|
||||
|
||||
/// Evaluates expression and stores the result to \p Value.
|
||||
///
|
||||
/// Returns true on success and false when the expression has invalid syntax.
|
||||
bool FileCheckPattern::EvaluateExpression(StringRef Expr, std::string &Value) const {
|
||||
// The only supported expression is @LINE([\+-]\d+)?
|
||||
if (!Expr.startswith("@LINE"))
|
||||
return false;
|
||||
void FileCheckPattern::evaluateExpression(StringRef Expr,
|
||||
std::string &Value) const {
|
||||
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;
|
||||
Expr.getAsInteger(10, Offset);
|
||||
}
|
||||
Value = llvm::itostr(LineNumber + Offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Matches the pattern string against the input buffer \p Buffer
|
||||
|
@ -320,8 +364,7 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
|
|||
std::string Value;
|
||||
|
||||
if (VariableUse.first[0] == '@') {
|
||||
if (!EvaluateExpression(VariableUse.first, Value))
|
||||
return StringRef::npos;
|
||||
evaluateExpression(VariableUse.first, Value);
|
||||
} else {
|
||||
llvm::Optional<StringRef> ValueRef =
|
||||
Context->getVarValue(VariableUse.first);
|
||||
|
@ -397,14 +440,10 @@ void FileCheckPattern::printVariableUses(const SourceMgr &SM, StringRef Buffer,
|
|||
StringRef Var = VariableUse.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) << "\"";
|
||||
}
|
||||
evaluateExpression(Var, Value);
|
||||
OS << "with expression \"";
|
||||
OS.write_escaped(Var) << "\" equal to \"";
|
||||
OS.write_escaped(Value) << "\"";
|
||||
} else {
|
||||
llvm::Optional<StringRef> VarValue = Context->getVarValue(Var);
|
||||
|
||||
|
|
|
@ -1,15 +1,56 @@
|
|||
; RUN: FileCheck -input-file %s %s
|
||||
; RUN: not FileCheck -check-prefix BAD -input-file %s %s
|
||||
3
|
||||
4 aaa
|
||||
5 bbb
|
||||
6 ccc
|
||||
7 CHECK: [[@LINE-3]] {{a}}aa
|
||||
8 CHECK: [[@LINE-3]] {{b}}bb
|
||||
9 CHECK: [[@LINE-3]] {{c}}cc
|
||||
10 foobar
|
||||
11 CHECK: [[@LINE-1]] {{foo}}bar
|
||||
12
|
||||
13 arst CHECK: [[@LINE]] {{a}}rst
|
||||
14
|
||||
15 BAD: [[@LINE:cant-have-regex]]
|
||||
; RUN: not FileCheck -check-prefix BAD1 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR1 %s
|
||||
; RUN: not FileCheck -check-prefix BAD2 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR2 %s
|
||||
; RUN: not FileCheck -check-prefix BAD3 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR3 %s
|
||||
; RUN: not FileCheck -check-prefix BAD4 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR4 %s
|
||||
; RUN: not FileCheck -check-prefix BAD5 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR5 %s
|
||||
; RUN: not FileCheck -check-prefix BAD6 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR6 %s
|
||||
; RUN: not FileCheck -check-prefix BAD7 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR7 %s
|
||||
; RUN: not FileCheck -check-prefix BAD8 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR8 %s
|
||||
; RUN: not FileCheck -check-prefix BAD9 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR9 %s
|
||||
; RUN: not FileCheck -check-prefix BAD10 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR10 %s
|
||||
; RUN: not FileCheck -check-prefix BAD10 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR10 %s
|
||||
13
|
||||
14 aaa
|
||||
15 bbb
|
||||
16 ccc
|
||||
17 CHECK: [[@LINE-3]] {{a}}aa
|
||||
18 CHECK: [[@LINE-3]] {{b}}bb
|
||||
19 CHECK: [[@LINE-3]] {{c}}cc
|
||||
20 foobar
|
||||
21 CHECK: [[@LINE-1]] {{foo}}bar
|
||||
22
|
||||
23 arst CHECK: [[@LINE]] {{a}}rst
|
||||
24
|
||||
25 BAD1: [[@LINE:cant-have-regex]]
|
||||
26 ERR1: line-count.txt:[[@LINE-1]]:12: error: invalid name in named regex definition
|
||||
27
|
||||
28 BAD2: [[ @LINE]]
|
||||
29 ERR2: line-count.txt:[[@LINE-1]]:12: error: unexpected whitespace
|
||||
30
|
||||
31 BAD3: [[@LINE ]]
|
||||
32 ERR3: line-count.txt:[[@LINE-1]]:17: error: unexpected whitespace
|
||||
33
|
||||
34 BAD4: [[ @LINE-1]]
|
||||
35 ERR4: line-count.txt:[[@LINE-1]]:12: error: unexpected whitespace
|
||||
36
|
||||
37 BAD5: [[@LINE -1]]
|
||||
38 ERR5: line-count.txt:[[@LINE-1]]:17: error: unexpected whitespace
|
||||
39
|
||||
40 BAD6: [[@LINE- 1]]
|
||||
41 ERR6: line-count.txt:[[@LINE-1]]:18: error: unexpected whitespace
|
||||
42
|
||||
43 BAD7: [[@LINE-1 ]]
|
||||
44 ERR7: line-count.txt:[[@LINE-1]]:19: error: unexpected whitespace
|
||||
45
|
||||
46 BAD8: [[@LIN]]
|
||||
47 ERR8: line-count.txt:[[@LINE-1]]:12: error: invalid pseudo variable '@LIN'
|
||||
48
|
||||
49 BAD9: [[@LINE*2]]
|
||||
50 ERR9: line-count.txt:[[@LINE-1]]:17: error: unsupported numeric operation '*'
|
||||
51
|
||||
52 BAD10: [[@LINE-x]]
|
||||
53 ERR10: line-count.txt:[[@LINE-1]]:19: error: invalid offset in numeric expression 'x'
|
||||
54
|
||||
55 BAD11: [[@LINE-1x]]
|
||||
56 ERR11: line-count.txt:[[@LINE-1]]:19: error: unexpected characters at end of numeric expression 'x'
|
||||
|
|
|
@ -90,6 +90,62 @@ TEST_F(FileCheckTest, ParseVar) {
|
|||
EXPECT_EQ(TrailIdx, VarName.size() - 1);
|
||||
}
|
||||
|
||||
class ExprTester {
|
||||
private:
|
||||
SourceMgr SM;
|
||||
FileCheckPatternContext Context;
|
||||
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
|
||||
|
||||
public:
|
||||
bool parseExpect(std::string &VarName, std::string &Trailer) {
|
||||
StringRef NameTrailer = StringRef(VarName + Trailer);
|
||||
std::unique_ptr<MemoryBuffer> Buffer =
|
||||
MemoryBuffer::getMemBufferCopy(NameTrailer, "TestBuffer");
|
||||
StringRef NameTrailerRef = Buffer->getBuffer();
|
||||
SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
|
||||
StringRef VarNameRef = NameTrailerRef.substr(0, VarName.size());
|
||||
StringRef TrailerRef = NameTrailerRef.substr(VarName.size());
|
||||
return P.parseExpression(VarNameRef, TrailerRef, SM);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(FileCheckTest, ParseExpr) {
|
||||
ExprTester Tester;
|
||||
|
||||
// @LINE with offset.
|
||||
std::string VarName = "@LINE";
|
||||
std::string Trailer = "+3";
|
||||
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
|
||||
|
||||
// @LINE only.
|
||||
Trailer = "";
|
||||
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
|
||||
|
||||
// Wrong Pseudovar.
|
||||
VarName = "@FOO";
|
||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
||||
|
||||
// Unsupported operator.
|
||||
VarName = "@LINE";
|
||||
Trailer = "/2";
|
||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
||||
|
||||
// Missing offset operand.
|
||||
VarName = "@LINE";
|
||||
Trailer = "+";
|
||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
||||
|
||||
// Cannot parse offset operand.
|
||||
VarName = "@LINE";
|
||||
Trailer = "+x";
|
||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
||||
|
||||
// Unexpected string at end of numeric expression.
|
||||
VarName = "@LINE";
|
||||
Trailer = "+5x";
|
||||
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
|
||||
}
|
||||
|
||||
TEST_F(FileCheckTest, FileCheckContext) {
|
||||
FileCheckPatternContext Cxt = FileCheckPatternContext();
|
||||
std::vector<std::string> GlobalDefines;
|
||||
|
|
Loading…
Reference in New Issue