mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +01:00
393fb2cc2e
Summary: Commit r366897 introduced the possibility to set a variable from an expression, such as [[#VAR2:VAR1+3]]. While introducing this feature, it introduced extra logic to allow using such a variable on the same line later on. Unfortunately that extra logic is flawed as it relies on a mapping from variable to expression defining it when the mapping is from variable definition to expression. This flaw causes among other issues PR42896. This commit avoids the problem by forbidding all use of a variable defined on the same line, and removes the now useless logic. Redesign will be done in a later commit because it will require some amount of refactoring first for the solution to be clean. One example is the need for some sort of transaction mechanism to set a variable temporarily and from an expression and rollback if the CHECK pattern does not match so that diagnostics show the right variable values. Reviewers: jhenderson, chandlerc, jdenny, probinson, grimar, arichardson, rnk Subscribers: JonChesterfield, rogfer01, hfinkel, kristina, rnk, tra, arichardson, grimar, dblaikie, probinson, llvm-commits, hiraditya Tags: #llvm Differential Revision: https://reviews.llvm.org/D66141 llvm-svn: 370663
604 lines
23 KiB
C++
604 lines
23 KiB
C++
//===- llvm/unittest/Support/FileCheckTest.cpp - FileCheck tests --===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/FileCheck.h"
|
|
#include "gtest/gtest.h"
|
|
#include <unordered_set>
|
|
|
|
using namespace llvm;
|
|
namespace {
|
|
|
|
class FileCheckTest : public ::testing::Test {};
|
|
|
|
TEST_F(FileCheckTest, Literal) {
|
|
// Eval returns the literal's value.
|
|
FileCheckExpressionLiteral Ten(10);
|
|
Expected<uint64_t> Value = Ten.eval();
|
|
ASSERT_TRUE(bool(Value));
|
|
EXPECT_EQ(10U, *Value);
|
|
|
|
// Max value can be correctly represented.
|
|
FileCheckExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
|
|
Value = Max.eval();
|
|
ASSERT_TRUE(bool(Value));
|
|
EXPECT_EQ(std::numeric_limits<uint64_t>::max(), *Value);
|
|
}
|
|
|
|
static std::string toString(const std::unordered_set<std::string> &Set) {
|
|
bool First = true;
|
|
std::string Str;
|
|
for (StringRef S : Set) {
|
|
Str += Twine(First ? "{" + S : ", " + S).str();
|
|
First = false;
|
|
}
|
|
Str += '}';
|
|
return Str;
|
|
}
|
|
|
|
static void
|
|
expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
|
|
Error Err) {
|
|
handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) {
|
|
ExpectedUndefVarNames.erase(E.getVarName());
|
|
});
|
|
EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
|
|
}
|
|
|
|
// Return whether Err contains any FileCheckUndefVarError whose associated name
|
|
// is not ExpectedUndefVarName.
|
|
static void expectUndefError(const Twine &ExpectedUndefVarName, Error Err) {
|
|
expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err));
|
|
}
|
|
|
|
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
|
|
|
|
TEST_F(FileCheckTest, NumericVariable) {
|
|
// Undefined variable: getValue and eval fail, error returned by eval holds
|
|
// the name of the undefined variable.
|
|
FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1);
|
|
EXPECT_EQ("FOO", FooVar.getName());
|
|
FileCheckNumericVariableUse FooVarUse =
|
|
FileCheckNumericVariableUse("FOO", &FooVar);
|
|
EXPECT_FALSE(FooVar.getValue());
|
|
Expected<uint64_t> EvalResult = FooVarUse.eval();
|
|
ASSERT_FALSE(EvalResult);
|
|
expectUndefError("FOO", EvalResult.takeError());
|
|
|
|
FooVar.setValue(42);
|
|
|
|
// Defined variable: getValue and eval return value set.
|
|
Optional<uint64_t> Value = FooVar.getValue();
|
|
ASSERT_TRUE(bool(Value));
|
|
EXPECT_EQ(42U, *Value);
|
|
EvalResult = FooVarUse.eval();
|
|
ASSERT_TRUE(bool(EvalResult));
|
|
EXPECT_EQ(42U, *EvalResult);
|
|
|
|
// Clearing variable: getValue and eval fail. Error returned by eval holds
|
|
// the name of the cleared variable.
|
|
FooVar.clearValue();
|
|
EXPECT_FALSE(FooVar.getValue());
|
|
EvalResult = FooVarUse.eval();
|
|
ASSERT_FALSE(EvalResult);
|
|
expectUndefError("FOO", EvalResult.takeError());
|
|
}
|
|
|
|
TEST_F(FileCheckTest, Binop) {
|
|
FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1);
|
|
FooVar.setValue(42);
|
|
std::unique_ptr<FileCheckNumericVariableUse> FooVarUse =
|
|
std::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar);
|
|
FileCheckNumericVariable BarVar = FileCheckNumericVariable("BAR", 2);
|
|
BarVar.setValue(18);
|
|
std::unique_ptr<FileCheckNumericVariableUse> BarVarUse =
|
|
std::make_unique<FileCheckNumericVariableUse>("BAR", &BarVar);
|
|
FileCheckASTBinop Binop =
|
|
FileCheckASTBinop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
|
|
|
|
// Defined variable: eval returns right value.
|
|
Expected<uint64_t> Value = Binop.eval();
|
|
ASSERT_TRUE(bool(Value));
|
|
EXPECT_EQ(60U, *Value);
|
|
|
|
// 1 undefined variable: eval fails, error contains name of undefined
|
|
// variable.
|
|
FooVar.clearValue();
|
|
Value = Binop.eval();
|
|
ASSERT_FALSE(Value);
|
|
expectUndefError("FOO", Value.takeError());
|
|
|
|
// 2 undefined variables: eval fails, error contains names of all undefined
|
|
// variables.
|
|
BarVar.clearValue();
|
|
Value = Binop.eval();
|
|
ASSERT_FALSE(Value);
|
|
expectUndefErrors({"FOO", "BAR"}, Value.takeError());
|
|
}
|
|
|
|
TEST_F(FileCheckTest, ValidVarNameStart) {
|
|
EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('a'));
|
|
EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('G'));
|
|
EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('_'));
|
|
EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('2'));
|
|
EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('$'));
|
|
EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('@'));
|
|
EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('+'));
|
|
EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('-'));
|
|
EXPECT_FALSE(FileCheckPattern::isValidVarNameStart(':'));
|
|
}
|
|
|
|
static StringRef bufferize(SourceMgr &SM, StringRef Str) {
|
|
std::unique_ptr<MemoryBuffer> Buffer =
|
|
MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
|
|
StringRef StrBufferRef = Buffer->getBuffer();
|
|
SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
|
|
return StrBufferRef;
|
|
}
|
|
|
|
TEST_F(FileCheckTest, ParseVar) {
|
|
SourceMgr SM;
|
|
StringRef OrigVarName = bufferize(SM, "GoodVar42");
|
|
StringRef VarName = OrigVarName;
|
|
Expected<FileCheckPattern::VariableProperties> ParsedVarResult =
|
|
FileCheckPattern::parseVariable(VarName, SM);
|
|
ASSERT_TRUE(bool(ParsedVarResult));
|
|
EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
|
|
EXPECT_TRUE(VarName.empty());
|
|
EXPECT_FALSE(ParsedVarResult->IsPseudo);
|
|
|
|
VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar");
|
|
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
|
|
ASSERT_TRUE(bool(ParsedVarResult));
|
|
EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
|
|
EXPECT_TRUE(VarName.empty());
|
|
EXPECT_FALSE(ParsedVarResult->IsPseudo);
|
|
|
|
VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar");
|
|
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
|
|
ASSERT_TRUE(bool(ParsedVarResult));
|
|
EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
|
|
EXPECT_TRUE(VarName.empty());
|
|
EXPECT_TRUE(ParsedVarResult->IsPseudo);
|
|
|
|
VarName = bufferize(SM, "42BadVar");
|
|
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
|
|
EXPECT_TRUE(errorToBool(ParsedVarResult.takeError()));
|
|
|
|
VarName = bufferize(SM, "$@");
|
|
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
|
|
EXPECT_TRUE(errorToBool(ParsedVarResult.takeError()));
|
|
|
|
VarName = OrigVarName = bufferize(SM, "B@dVar");
|
|
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
|
|
ASSERT_TRUE(bool(ParsedVarResult));
|
|
EXPECT_EQ(VarName, OrigVarName.substr(1));
|
|
EXPECT_EQ(ParsedVarResult->Name, "B");
|
|
EXPECT_FALSE(ParsedVarResult->IsPseudo);
|
|
|
|
VarName = OrigVarName = bufferize(SM, "B$dVar");
|
|
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
|
|
ASSERT_TRUE(bool(ParsedVarResult));
|
|
EXPECT_EQ(VarName, OrigVarName.substr(1));
|
|
EXPECT_EQ(ParsedVarResult->Name, "B");
|
|
EXPECT_FALSE(ParsedVarResult->IsPseudo);
|
|
|
|
VarName = bufferize(SM, "BadVar+");
|
|
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
|
|
ASSERT_TRUE(bool(ParsedVarResult));
|
|
EXPECT_EQ(VarName, "+");
|
|
EXPECT_EQ(ParsedVarResult->Name, "BadVar");
|
|
EXPECT_FALSE(ParsedVarResult->IsPseudo);
|
|
|
|
VarName = bufferize(SM, "BadVar-");
|
|
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
|
|
ASSERT_TRUE(bool(ParsedVarResult));
|
|
EXPECT_EQ(VarName, "-");
|
|
EXPECT_EQ(ParsedVarResult->Name, "BadVar");
|
|
EXPECT_FALSE(ParsedVarResult->IsPseudo);
|
|
|
|
VarName = bufferize(SM, "BadVar:");
|
|
ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
|
|
ASSERT_TRUE(bool(ParsedVarResult));
|
|
EXPECT_EQ(VarName, ":");
|
|
EXPECT_EQ(ParsedVarResult->Name, "BadVar");
|
|
EXPECT_FALSE(ParsedVarResult->IsPseudo);
|
|
}
|
|
|
|
class PatternTester {
|
|
private:
|
|
size_t LineNumber = 1;
|
|
SourceMgr SM;
|
|
FileCheckRequest Req;
|
|
FileCheckPatternContext Context;
|
|
FileCheckPattern P =
|
|
FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);
|
|
|
|
public:
|
|
PatternTester() {
|
|
std::vector<std::string> GlobalDefines;
|
|
GlobalDefines.emplace_back(std::string("#FOO=42"));
|
|
GlobalDefines.emplace_back(std::string("BAR=BAZ"));
|
|
// An ASSERT_FALSE would make more sense but cannot be used in a
|
|
// constructor.
|
|
EXPECT_FALSE(
|
|
errorToBool(Context.defineCmdlineVariables(GlobalDefines, SM)));
|
|
Context.createLineVariable();
|
|
// Call parsePattern to have @LINE defined.
|
|
P.parsePattern("N/A", "CHECK", SM, Req);
|
|
// parsePattern does not expect to be called twice for the same line and
|
|
// will set FixedStr and RegExStr incorrectly if it is. Therefore prepare
|
|
// a pattern for a different line.
|
|
initNextPattern();
|
|
}
|
|
|
|
void initNextPattern() {
|
|
P = FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);
|
|
}
|
|
|
|
bool parseSubstExpect(StringRef Expr) {
|
|
StringRef ExprBufferRef = bufferize(SM, Expr);
|
|
Optional<FileCheckNumericVariable *> DefinedNumericVariable;
|
|
return errorToBool(
|
|
P.parseNumericSubstitutionBlock(ExprBufferRef, DefinedNumericVariable,
|
|
false, LineNumber - 1, &Context, SM)
|
|
.takeError());
|
|
}
|
|
|
|
bool parsePatternExpect(StringRef Pattern) {
|
|
StringRef PatBufferRef = bufferize(SM, Pattern);
|
|
return P.parsePattern(PatBufferRef, "CHECK", SM, Req);
|
|
}
|
|
|
|
bool matchExpect(StringRef Buffer) {
|
|
StringRef BufferRef = bufferize(SM, Buffer);
|
|
size_t MatchLen;
|
|
return errorToBool(P.match(BufferRef, MatchLen, SM).takeError());
|
|
}
|
|
};
|
|
|
|
TEST_F(FileCheckTest, ParseExpr) {
|
|
PatternTester Tester;
|
|
|
|
// Variable definition.
|
|
|
|
// Definition of invalid variable.
|
|
EXPECT_TRUE(Tester.parseSubstExpect("10VAR:"));
|
|
EXPECT_TRUE(Tester.parseSubstExpect("@FOO:"));
|
|
EXPECT_TRUE(Tester.parseSubstExpect("@LINE:"));
|
|
|
|
// Conflict with pattern variable.
|
|
EXPECT_TRUE(Tester.parseSubstExpect("BAR:"));
|
|
|
|
// Garbage after name of variable being defined.
|
|
EXPECT_TRUE(Tester.parseSubstExpect("VAR GARBAGE:"));
|
|
|
|
// Acceptable variable definition.
|
|
EXPECT_FALSE(Tester.parseSubstExpect("VAR1:"));
|
|
EXPECT_FALSE(Tester.parseSubstExpect(" VAR2:"));
|
|
EXPECT_FALSE(Tester.parseSubstExpect("VAR3 :"));
|
|
EXPECT_FALSE(Tester.parseSubstExpect("VAR3: "));
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOOBAR: FOO+1]]"));
|
|
|
|
// Numeric expression.
|
|
|
|
// Unacceptable variable.
|
|
EXPECT_TRUE(Tester.parseSubstExpect("10VAR"));
|
|
EXPECT_TRUE(Tester.parseSubstExpect("@FOO"));
|
|
|
|
// Only valid variable.
|
|
EXPECT_FALSE(Tester.parseSubstExpect("@LINE"));
|
|
EXPECT_FALSE(Tester.parseSubstExpect("FOO"));
|
|
EXPECT_FALSE(Tester.parseSubstExpect("UNDEF"));
|
|
|
|
// Valid empty expression.
|
|
EXPECT_FALSE(Tester.parseSubstExpect(""));
|
|
|
|
// Invalid use of variable defined on the same line from expression. Note
|
|
// that the same pattern object is used for the parsePatternExpect and
|
|
// parseSubstExpect since no initNextPattern is called, thus appearing as
|
|
// being on the same line from the pattern's point of view.
|
|
ASSERT_FALSE(Tester.parsePatternExpect("[[#LINE1VAR:FOO+1]]"));
|
|
EXPECT_TRUE(Tester.parseSubstExpect("LINE1VAR"));
|
|
|
|
// Invalid use of variable defined on same line from input. As above, the
|
|
// absence of a call to initNextPattern makes it appear to be on the same
|
|
// line from the pattern's point of view.
|
|
ASSERT_FALSE(Tester.parsePatternExpect("[[#LINE2VAR:]]"));
|
|
EXPECT_TRUE(Tester.parseSubstExpect("LINE2VAR"));
|
|
|
|
// Unsupported operator.
|
|
EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2"));
|
|
|
|
// Missing offset operand.
|
|
EXPECT_TRUE(Tester.parseSubstExpect("@LINE+"));
|
|
|
|
// Valid expression.
|
|
EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5"));
|
|
EXPECT_FALSE(Tester.parseSubstExpect("FOO+4"));
|
|
Tester.initNextPattern();
|
|
EXPECT_FALSE(Tester.parseSubstExpect("FOOBAR"));
|
|
EXPECT_FALSE(Tester.parseSubstExpect("LINE1VAR"));
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]"));
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]"));
|
|
}
|
|
|
|
TEST_F(FileCheckTest, ParsePattern) {
|
|
PatternTester Tester;
|
|
|
|
// Space in pattern variable expression.
|
|
EXPECT_TRUE(Tester.parsePatternExpect("[[ BAR]]"));
|
|
|
|
// Invalid variable name.
|
|
EXPECT_TRUE(Tester.parsePatternExpect("[[42INVALID]]"));
|
|
|
|
// Invalid pattern variable definition.
|
|
EXPECT_TRUE(Tester.parsePatternExpect("[[@PAT:]]"));
|
|
EXPECT_TRUE(Tester.parsePatternExpect("[[PAT+2:]]"));
|
|
|
|
// Collision with numeric variable.
|
|
EXPECT_TRUE(Tester.parsePatternExpect("[[FOO:]]"));
|
|
|
|
// Valid use of pattern variable.
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[BAR]]"));
|
|
|
|
// Valid pattern variable definition.
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[PAT:[0-9]+]]"));
|
|
|
|
// Invalid numeric expressions.
|
|
EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]"));
|
|
EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]"));
|
|
EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]"));
|
|
|
|
// Valid numeric expressions and numeric variable definition.
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO]]"));
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE+2]]"));
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[#NUMVAR:]]"));
|
|
}
|
|
|
|
TEST_F(FileCheckTest, Match) {
|
|
PatternTester Tester;
|
|
|
|
// Check matching an empty expression only matches a number.
|
|
Tester.parsePatternExpect("[[#]]");
|
|
EXPECT_TRUE(Tester.matchExpect("FAIL"));
|
|
EXPECT_FALSE(Tester.matchExpect("18"));
|
|
|
|
// Check matching a definition only matches a number.
|
|
Tester.initNextPattern();
|
|
Tester.parsePatternExpect("[[#NUMVAR:]]");
|
|
EXPECT_TRUE(Tester.matchExpect("FAIL"));
|
|
EXPECT_TRUE(Tester.matchExpect(""));
|
|
EXPECT_FALSE(Tester.matchExpect("18"));
|
|
|
|
// Check matching the variable defined matches the correct number only
|
|
Tester.initNextPattern();
|
|
Tester.parsePatternExpect("[[#NUMVAR]] [[#NUMVAR+2]]");
|
|
EXPECT_TRUE(Tester.matchExpect("19 21"));
|
|
EXPECT_TRUE(Tester.matchExpect("18 21"));
|
|
EXPECT_FALSE(Tester.matchExpect("18 20"));
|
|
|
|
// Check matching a numeric expression using @LINE after match failure uses
|
|
// the correct value for @LINE.
|
|
Tester.initNextPattern();
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]"));
|
|
// Ok, @LINE is 5 now.
|
|
EXPECT_FALSE(Tester.matchExpect("5"));
|
|
Tester.initNextPattern();
|
|
// @LINE is now 6, match with substitution failure.
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[#UNKNOWN]]"));
|
|
EXPECT_TRUE(Tester.matchExpect("FOO"));
|
|
Tester.initNextPattern();
|
|
// Check that @LINE is 7 as expected.
|
|
EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]"));
|
|
EXPECT_FALSE(Tester.matchExpect("7"));
|
|
}
|
|
|
|
TEST_F(FileCheckTest, Substitution) {
|
|
SourceMgr SM;
|
|
FileCheckPatternContext Context;
|
|
std::vector<std::string> GlobalDefines;
|
|
GlobalDefines.emplace_back(std::string("FOO=BAR"));
|
|
EXPECT_FALSE(errorToBool(Context.defineCmdlineVariables(GlobalDefines, SM)));
|
|
|
|
// Substitution of an undefined string variable fails and error holds that
|
|
// variable's name.
|
|
FileCheckStringSubstitution StringSubstitution =
|
|
FileCheckStringSubstitution(&Context, "VAR404", 42);
|
|
Expected<std::string> SubstValue = StringSubstitution.getResult();
|
|
ASSERT_FALSE(bool(SubstValue));
|
|
expectUndefError("VAR404", SubstValue.takeError());
|
|
|
|
// Substitutions of defined pseudo and non-pseudo numeric variables return
|
|
// the right value.
|
|
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 1);
|
|
FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 1);
|
|
LineVar.setValue(42);
|
|
NVar.setValue(10);
|
|
auto LineVarUse =
|
|
std::make_unique<FileCheckNumericVariableUse>("@LINE", &LineVar);
|
|
auto NVarUse = std::make_unique<FileCheckNumericVariableUse>("N", &NVar);
|
|
FileCheckNumericSubstitution SubstitutionLine = FileCheckNumericSubstitution(
|
|
&Context, "@LINE", std::move(LineVarUse), 12);
|
|
FileCheckNumericSubstitution SubstitutionN =
|
|
FileCheckNumericSubstitution(&Context, "N", std::move(NVarUse), 30);
|
|
SubstValue = SubstitutionLine.getResult();
|
|
ASSERT_TRUE(bool(SubstValue));
|
|
EXPECT_EQ("42", *SubstValue);
|
|
SubstValue = SubstitutionN.getResult();
|
|
ASSERT_TRUE(bool(SubstValue));
|
|
EXPECT_EQ("10", *SubstValue);
|
|
|
|
// Substitution of an undefined numeric variable fails, error holds name of
|
|
// undefined variable.
|
|
LineVar.clearValue();
|
|
SubstValue = SubstitutionLine.getResult();
|
|
ASSERT_FALSE(bool(SubstValue));
|
|
expectUndefError("@LINE", SubstValue.takeError());
|
|
NVar.clearValue();
|
|
SubstValue = SubstitutionN.getResult();
|
|
ASSERT_FALSE(bool(SubstValue));
|
|
expectUndefError("N", SubstValue.takeError());
|
|
|
|
// Substitution of a defined string variable returns the right value.
|
|
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1);
|
|
StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
|
|
SubstValue = StringSubstitution.getResult();
|
|
ASSERT_TRUE(bool(SubstValue));
|
|
EXPECT_EQ("BAR", *SubstValue);
|
|
}
|
|
|
|
TEST_F(FileCheckTest, FileCheckContext) {
|
|
FileCheckPatternContext Cxt = FileCheckPatternContext();
|
|
std::vector<std::string> GlobalDefines;
|
|
SourceMgr SM;
|
|
|
|
// Missing equal sign.
|
|
GlobalDefines.emplace_back(std::string("LocalVar"));
|
|
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
GlobalDefines.clear();
|
|
GlobalDefines.emplace_back(std::string("#LocalNumVar"));
|
|
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
|
|
// Empty variable name.
|
|
GlobalDefines.clear();
|
|
GlobalDefines.emplace_back(std::string("=18"));
|
|
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
GlobalDefines.clear();
|
|
GlobalDefines.emplace_back(std::string("#=18"));
|
|
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
|
|
// Invalid variable name.
|
|
GlobalDefines.clear();
|
|
GlobalDefines.emplace_back(std::string("18LocalVar=18"));
|
|
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
GlobalDefines.clear();
|
|
GlobalDefines.emplace_back(std::string("#18LocalNumVar=18"));
|
|
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
|
|
// Name conflict between pattern and numeric variable.
|
|
GlobalDefines.clear();
|
|
GlobalDefines.emplace_back(std::string("LocalVar=18"));
|
|
GlobalDefines.emplace_back(std::string("#LocalVar=36"));
|
|
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
Cxt = FileCheckPatternContext();
|
|
GlobalDefines.clear();
|
|
GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
|
|
GlobalDefines.emplace_back(std::string("LocalNumVar=36"));
|
|
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
Cxt = FileCheckPatternContext();
|
|
|
|
// Invalid numeric value for numeric variable.
|
|
GlobalDefines.clear();
|
|
GlobalDefines.emplace_back(std::string("#LocalNumVar=x"));
|
|
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
|
|
// Define local variables from command-line.
|
|
GlobalDefines.clear();
|
|
// Clear local variables to remove dummy numeric variable x that
|
|
// parseNumericSubstitutionBlock would have created and stored in
|
|
// GlobalNumericVariableTable.
|
|
Cxt.clearLocalVars();
|
|
GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
|
|
GlobalDefines.emplace_back(std::string("EmptyVar="));
|
|
GlobalDefines.emplace_back(std::string("#LocalNumVar1=18"));
|
|
GlobalDefines.emplace_back(std::string("#LocalNumVar2=LocalNumVar1+2"));
|
|
ASSERT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
|
|
// Check defined variables are present and undefined is absent.
|
|
StringRef LocalVarStr = "LocalVar";
|
|
StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
|
|
StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
|
|
StringRef EmptyVarStr = "EmptyVar";
|
|
StringRef UnknownVarStr = "UnknownVar";
|
|
Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
|
|
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1);
|
|
Optional<FileCheckNumericVariable *> DefinedNumericVariable;
|
|
Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionAST =
|
|
P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
|
|
/*IsLegacyLineExpr=*/false,
|
|
/*LineNumber=*/1, &Cxt, SM);
|
|
ASSERT_TRUE(bool(LocalVar));
|
|
EXPECT_EQ(*LocalVar, "FOO");
|
|
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
|
Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
|
|
ASSERT_TRUE(bool(ExpressionAST));
|
|
Expected<uint64_t> ExpressionVal = (*ExpressionAST)->eval();
|
|
ASSERT_TRUE(bool(ExpressionVal));
|
|
EXPECT_EQ(*ExpressionVal, 18U);
|
|
ExpressionAST =
|
|
P.parseNumericSubstitutionBlock(LocalNumVar2Ref, DefinedNumericVariable,
|
|
/*IsLegacyLineExpr=*/false,
|
|
/*LineNumber=*/1, &Cxt, SM);
|
|
ASSERT_TRUE(bool(ExpressionAST));
|
|
ExpressionVal = (*ExpressionAST)->eval();
|
|
ASSERT_TRUE(bool(ExpressionVal));
|
|
EXPECT_EQ(*ExpressionVal, 20U);
|
|
ASSERT_TRUE(bool(EmptyVar));
|
|
EXPECT_EQ(*EmptyVar, "");
|
|
EXPECT_TRUE(errorToBool(UnknownVar.takeError()));
|
|
|
|
// Clear local variables and check they become absent.
|
|
Cxt.clearLocalVars();
|
|
LocalVar = Cxt.getPatternVarValue(LocalVarStr);
|
|
EXPECT_TRUE(errorToBool(LocalVar.takeError()));
|
|
// Check a numeric expression's evaluation fails if called after clearing of
|
|
// local variables, if it was created before. This is important because local
|
|
// variable clearing due to --enable-var-scope happens after numeric
|
|
// expressions are linked to the numeric variables they use.
|
|
EXPECT_TRUE(errorToBool((*ExpressionAST)->eval().takeError()));
|
|
P = FileCheckPattern(Check::CheckPlain, &Cxt, 2);
|
|
ExpressionAST = P.parseNumericSubstitutionBlock(
|
|
LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
|
|
/*LineNumber=*/2, &Cxt, SM);
|
|
ASSERT_TRUE(bool(ExpressionAST));
|
|
ExpressionVal = (*ExpressionAST)->eval();
|
|
EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
|
|
ExpressionAST = P.parseNumericSubstitutionBlock(
|
|
LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
|
|
/*LineNumber=*/2, &Cxt, SM);
|
|
ASSERT_TRUE(bool(ExpressionAST));
|
|
ExpressionVal = (*ExpressionAST)->eval();
|
|
EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
|
|
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
|
EXPECT_TRUE(errorToBool(EmptyVar.takeError()));
|
|
// Clear again because parseNumericSubstitutionBlock would have created a
|
|
// dummy variable and stored it in GlobalNumericVariableTable.
|
|
Cxt.clearLocalVars();
|
|
|
|
// Redefine global variables and check variables are defined again.
|
|
GlobalDefines.emplace_back(std::string("$GlobalVar=BAR"));
|
|
GlobalDefines.emplace_back(std::string("#$GlobalNumVar=36"));
|
|
ASSERT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
|
StringRef GlobalVarStr = "$GlobalVar";
|
|
StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");
|
|
Expected<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
|
ASSERT_TRUE(bool(GlobalVar));
|
|
EXPECT_EQ(*GlobalVar, "BAR");
|
|
P = FileCheckPattern(Check::CheckPlain, &Cxt, 3);
|
|
ExpressionAST = P.parseNumericSubstitutionBlock(
|
|
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
|
|
/*LineNumber=*/3, &Cxt, SM);
|
|
ASSERT_TRUE(bool(ExpressionAST));
|
|
ExpressionVal = (*ExpressionAST)->eval();
|
|
ASSERT_TRUE(bool(ExpressionVal));
|
|
EXPECT_EQ(*ExpressionVal, 36U);
|
|
|
|
// Clear local variables and check global variables remain defined.
|
|
Cxt.clearLocalVars();
|
|
EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError()));
|
|
P = FileCheckPattern(Check::CheckPlain, &Cxt, 4);
|
|
ExpressionAST = P.parseNumericSubstitutionBlock(
|
|
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
|
|
/*LineNumber=*/4, &Cxt, SM);
|
|
ASSERT_TRUE(bool(ExpressionAST));
|
|
ExpressionVal = (*ExpressionAST)->eval();
|
|
ASSERT_TRUE(bool(ExpressionVal));
|
|
EXPECT_EQ(*ExpressionVal, 36U);
|
|
}
|
|
} // namespace
|