266 changes: 157 additions & 109 deletions llvm/lib/Support/FileCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,31 @@

using namespace llvm;

llvm::Optional<std::string> FileCheckPatternSubstitution::getResult() const {
if (IsNumExpr) {
return utostr(NumExpr->getValue());
} else {
// Look up the value and escape it so that we can put it into the
// regex.
llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
if (!VarVal)
return llvm::None;
return Regex::escape(*VarVal);
}
}

StringRef FileCheckPatternSubstitution::getUndefVarName() const {
// Parsing guarantees only @LINE is ever referenced and it is not undefined
// by ClearLocalVars.
if (IsNumExpr)
return StringRef();

if (!Context->getPatternVarValue(FromStr))
return FromStr;

return StringRef();
}

bool FileCheckPattern::isValidVarNameStart(char C) {
return C == '_' || isalpha(C);
}
Expand Down Expand Up @@ -55,56 +80,69 @@ bool FileCheckPattern::parseVariable(StringRef Str, bool &IsPseudo,
return false;
}

// StringRef holding all characters considered as horizontal whitespaces by
// FileCheck input canonicalization.
StringRef SpaceChars = " \t";

// 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 {
FileCheckNumExpr *
FileCheckPattern::parseNumericExpression(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;
"invalid pseudo numeric variable '" + Name + "'");
return nullptr;
}

// Check if this is a supported operation and select function to perform it.
Trailer = Trailer.ltrim(SpaceChars);
if (Trailer.empty())
return false;
return Context->makeNumExpr(LineNumber);
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.
Trailer = Trailer.ltrim(SpaceChars);
if (Trailer.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
"missing operand in numeric expression '" + Trailer + "'");
return true;
return nullptr;
}
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;
return nullptr;
}
Trailer = Trailer.ltrim(SpaceChars);
if (!Trailer.empty()) {
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
"unexpected characters at end of numeric expression '" +
Trailer + "'");
return true;
return nullptr;
}
return false;

uint64_t Value;
switch (Operator) {
case '+':
Value = LineNumber + Offset;
break;
case '-':
Value = LineNumber - Offset;
break;
default:
SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
Twine("unsupported numeric operation '") + Twine(Operator) +
"'");
return nullptr;
}
return Context->makeNumExpr(Value);
}

/// Parses the given string into the Pattern.
Expand Down Expand Up @@ -194,75 +232,99 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
continue;
}

// Named RegEx matches. These are of two forms: [[foo:.*]] which matches .*
// (or some other regex) and assigns it to the FileCheck variable 'foo'. The
// 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.
// Pattern and numeric expression matches. Pattern expressions come in two
// forms: [[foo:.*]] and [[foo]]. The former matches .* (or some other
// regex) and assigns it to the FileCheck variable 'foo'. The latter
// substitutes foo's value. Numeric expressions start with a '#' sign after
// the double brackets and only have the substitution form. Pattern
// variables must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*"
// to be valid, as this helps catch some common errors. Numeric expressions
// only support the @LINE pseudo numeric variable.
if (PatternStr.startswith("[[")) {
StringRef MatchStr = PatternStr.substr(2);
StringRef UnparsedPatternStr = PatternStr.substr(2);
// 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(MatchStr, SM);
size_t End = FindRegexVarEnd(UnparsedPatternStr, SM);
StringRef MatchStr = UnparsedPatternStr.substr(0, End);
bool IsNumExpr = MatchStr.consume_front("#");
const char *RefTypeStr =
IsNumExpr ? "numeric expression" : "pattern variable";

if (End == StringRef::npos) {
SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
SourceMgr::DK_Error,
"invalid named regex reference, no ]] found");
SM.PrintMessage(
SMLoc::getFromPointer(PatternStr.data()), SourceMgr::DK_Error,
Twine("Invalid ") + RefTypeStr + " reference, no ]] found");
return true;
}

MatchStr = MatchStr.substr(0, End);
PatternStr = PatternStr.substr(End + 4);
// Strip the subtitution we are parsing. End points to the start of the
// "]]" closing the expression so account for it in computing the index
// of the first unparsed character.
PatternStr = UnparsedPatternStr.substr(End + 2);

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;
if (IsNumExpr)
MatchStr = MatchStr.ltrim(SpaceChars);
else {
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;
if (parseVariable(MatchStr, IsPseudo, TrailIdx)) {
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
SourceMgr::DK_Error, "invalid name in named regex");
SourceMgr::DK_Error, "invalid variable name");
return true;
}

size_t SubstInsertIdx = RegExStr.size();
FileCheckNumExpr *NumExpr;

StringRef Name = MatchStr.substr(0, TrailIdx);
StringRef Trailer = MatchStr.substr(TrailIdx);
bool IsVarDef = (VarEndIdx != StringRef::npos);

if (IsVarDef && (IsPseudo || !Trailer.consume_front(":"))) {
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
SourceMgr::DK_Error,
"invalid name in named regex definition");
"invalid name in pattern variable definition");
return true;
}

if (!IsVarDef && IsPseudo) {
if (parseExpression(Name, Trailer, SM))
NumExpr = parseNumericExpression(Name, Trailer, SM);
if (NumExpr == nullptr)
return true;
IsNumExpr = true;
}

// Handle [[foo]].
if (!IsVarDef) {
// 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) {
// Handle use of pattern variables that were defined earlier on the
// same line by emitting a backreference.
if (!IsNumExpr && VariableDefs.find(Name) != VariableDefs.end()) {
unsigned CaptureParen = VariableDefs[Name];
if (CaptureParen < 1 || CaptureParen > 9) {
SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
SourceMgr::DK_Error,
"Can't back-reference more than 9 variables");
return true;
}
AddBackrefToRegEx(VarParenNum);
AddBackrefToRegEx(CaptureParen);
} else {
VariableUses.push_back(std::make_pair(MatchStr, RegExStr.size()));
// Handle use of pattern variables ([[<var>]]) defined in previous
// CHECK pattern or use of a numeric expression.
FileCheckPatternSubstitution Substitution =
IsNumExpr ? FileCheckPatternSubstitution(Context, MatchStr,
NumExpr, SubstInsertIdx)
: FileCheckPatternSubstitution(Context, MatchStr,
SubstInsertIdx);
Substitutions.push_back(Substitution);
}
continue;
}
Expand Down Expand Up @@ -315,28 +377,15 @@ void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) {
RegExStr += Backref;
}

/// Evaluates expression and stores the result to \p Value.
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);
Expr.getAsInteger(10, Offset);
}
Value = llvm::itostr(LineNumber + Offset);
}

/// Matches the pattern string against the input buffer \p 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 \p
/// MatchLen.
///
/// The GlobalVariableTable StringMap in the FileCheckPatternContext class
/// instance provides the current values of FileCheck variables and is updated
/// if this match defines new values.
/// instance provides the current values of FileCheck pattern variables and is
/// updated if this match defines new values.
size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
// If this is the EOF pattern, match it immediately.
if (CheckTy == Check::CheckEOF) {
Expand All @@ -356,30 +405,23 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
// actual value.
StringRef RegExToMatch = RegExStr;
std::string TmpStr;
if (!VariableUses.empty()) {
if (!Substitutions.empty()) {
TmpStr = RegExStr;

unsigned InsertOffset = 0;
for (const auto &VariableUse : VariableUses) {
std::string Value;

if (VariableUse.first[0] == '@') {
evaluateExpression(VariableUse.first, Value);
} else {
llvm::Optional<StringRef> ValueRef =
Context->getVarValue(VariableUse.first);
// If the variable is undefined, return an error.
if (!ValueRef)
return StringRef::npos;

// Look up the value and escape it so that we can put it into the regex.
Value += Regex::escape(*ValueRef);
}
size_t InsertOffset = 0;
// Substitute all pattern variables and numeric expressions whose value is
// known just now. Use of pattern variables defined on the same line are
// handled by back-references.
for (const auto &Substitution : Substitutions) {
// Substitute and check for failure (e.g. use of undefined variable).
llvm::Optional<std::string> Value = Substitution.getResult();
if (!Value)
return StringRef::npos;

// Plop it into the regex at the adjusted offset.
TmpStr.insert(TmpStr.begin() + VariableUse.second + InsertOffset,
Value.begin(), Value.end());
InsertOffset += Value.size();
TmpStr.insert(TmpStr.begin() + Substitution.getIndex() + InsertOffset,
Value->begin(), Value->end());
InsertOffset += Value->size();
}

// Match the newly constructed regex.
Expand All @@ -394,7 +436,7 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
assert(!MatchInfo.empty() && "Didn't get any match");
StringRef FullMatch = MatchInfo[0];

// If this defines any variables, remember their values.
// If this defines any pattern variables, remember their values.
for (const auto &VariableDef : VariableDefs) {
assert(VariableDef.second < MatchInfo.size() && "Internal paren error");
Context->GlobalVariableTable[VariableDef.first] =
Expand Down Expand Up @@ -429,33 +471,33 @@ unsigned FileCheckPattern::computeMatchDistance(StringRef Buffer) const {
return BufferPrefix.edit_distance(ExampleString);
}

void FileCheckPattern::printVariableUses(const SourceMgr &SM, StringRef Buffer,
SMRange MatchRange) const {
// If this was a regular expression using variables, print the current
// variable values.
if (!VariableUses.empty()) {
for (const auto &VariableUse : VariableUses) {
void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
SMRange MatchRange) const {
// Print what we know about substitutions. This covers both uses of pattern
// variables and numeric subsitutions.
if (!Substitutions.empty()) {
for (const auto &Substitution : Substitutions) {
SmallString<256> Msg;
raw_svector_ostream OS(Msg);
StringRef Var = VariableUse.first;
if (Var[0] == '@') {
std::string Value;
evaluateExpression(Var, Value);
OS << "with expression \"";
OS.write_escaped(Var) << "\" equal to \"";
OS.write_escaped(Value) << "\"";
bool IsNumExpr = Substitution.isNumExpr();
llvm::Optional<std::string> MatchedValue = Substitution.getResult();

// Substitution failed or is not known at match time, print the undefined
// variable it uses.
if (!MatchedValue) {
StringRef UndefVarName = Substitution.getUndefVarName();
if (UndefVarName.empty())
continue;
OS << "uses undefined variable \"";
OS.write_escaped(UndefVarName) << "\"";
} else {
llvm::Optional<StringRef> VarValue = Context->getVarValue(Var);

// Check for undefined variable references.
if (!VarValue) {
OS << "uses undefined variable \"";
OS.write_escaped(Var) << "\"";
} else {
// Substitution succeeded. Print substituted value.
if (IsNumExpr)
OS << "with numeric expression \"";
else
OS << "with variable \"";
OS.write_escaped(Var) << "\" equal to \"";
OS.write_escaped(*VarValue) << "\"";
}
OS.write_escaped(Substitution.getFromString()) << "\" equal to \"";
OS.write_escaped(*MatchedValue) << "\"";
}

if (MatchRange.isValid())
Expand Down Expand Up @@ -534,14 +576,20 @@ void FileCheckPattern::printFuzzyMatch(
}

llvm::Optional<StringRef>
FileCheckPatternContext::getVarValue(StringRef VarName) {
FileCheckPatternContext::getPatternVarValue(StringRef VarName) {
auto VarIter = GlobalVariableTable.find(VarName);
if (VarIter == GlobalVariableTable.end())
return llvm::None;

return VarIter->second;
}

template <class... Types>
FileCheckNumExpr *FileCheckPatternContext::makeNumExpr(Types... Args) {
NumExprs.emplace_back(new FileCheckNumExpr(Args...));
return NumExprs.back().get();
}

/// Finds the closing sequence of a regex variable usage or definition.
///
/// \p Str has to point in the beginning of the definition (right after the
Expand Down Expand Up @@ -998,7 +1046,7 @@ static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
{MatchRange});
Pat.printVariableUses(SM, Buffer, MatchRange);
Pat.printSubstitutions(SM, Buffer, MatchRange);
}

static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
Expand Down Expand Up @@ -1049,7 +1097,7 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here");

// Allow the pattern to print additional information if desired.
Pat.printVariableUses(SM, Buffer);
Pat.printSubstitutions(SM, Buffer);

if (ExpectedMatch)
Pat.printFuzzyMatch(SM, Buffer, Diags);
Expand Down
34 changes: 22 additions & 12 deletions llvm/test/FileCheck/line-count.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: FileCheck -input-file %s %s
; RUN: FileCheck -input-file %s %s
; 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
Expand All @@ -23,34 +23,44 @@
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
26 ERR1: line-count.txt:[[#@LINE-1]]:12: error: invalid name in pattern variable definition
27
28 BAD2: [[ @LINE]]
29 ERR2: line-count.txt:[[@LINE-1]]:12: error: unexpected whitespace
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
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
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
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
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
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'
47 ERR8: line-count.txt:[[#@LINE-1]]:12: error: invalid pseudo numeric variable '@LIN'
48
49 BAD9: [[@LINE*2]]
50 ERR9: line-count.txt:[[@LINE-1]]:17: error: unsupported numeric operation '*'
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'
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'
56 ERR11: line-count.txt:[[#@LINE-1]]:19: error: unexpected characters at end of numeric expression 'x'
57
58 CHECK: [[#@LINE]] CHECK
59 CHECK: [[# @LINE]] CHECK
60 CHECK: [[# @LINE ]] CHECK
61
62 CHECK: [[#@LINE-1]]
63 CHECK: [[# @LINE-1]] CHECK
64 CHECK: [[# @LINE -1]] CHECK
65 CHECK: [[# @LINE - 1]] CHECK
66 CHECK: [[# @LINE - 1 ]] CHECK
62 changes: 54 additions & 8 deletions llvm/unittests/Support/FileCheckTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class ExprTester {
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);
return P.parseNumericExpression(VarNameRef, TrailerRef, SM) == nullptr;
}
};

Expand Down Expand Up @@ -146,6 +146,52 @@ TEST_F(FileCheckTest, ParseExpr) {
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
}

TEST_F(FileCheckTest, Substitution) {
SourceMgr SM;
FileCheckPatternContext Context;
std::vector<std::string> GlobalDefines;
GlobalDefines.emplace_back(std::string("FOO=BAR"));
Context.defineCmdlineVariables(GlobalDefines, SM);

FileCheckPatternSubstitution Substitution =
FileCheckPatternSubstitution(&Context, "VAR404", 42);
EXPECT_FALSE(Substitution.getResult());

FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
llvm::Optional<std::string> Value = Substitution.getResult();
EXPECT_TRUE(Value);
EXPECT_EQ("42", *Value);

FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
Value = Substitution.getResult();
EXPECT_TRUE(Value);
EXPECT_EQ("BAR", *Value);
}

TEST_F(FileCheckTest, UndefVars) {
SourceMgr SM;
FileCheckPatternContext Context;
std::vector<std::string> GlobalDefines;
GlobalDefines.emplace_back(std::string("FOO=BAR"));
Context.defineCmdlineVariables(GlobalDefines, SM);

FileCheckPatternSubstitution Substitution =
FileCheckPatternSubstitution(&Context, "VAR404", 42);
StringRef UndefVar = Substitution.getUndefVarName();
EXPECT_EQ("VAR404", UndefVar);

FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
UndefVar = Substitution.getUndefVarName();
EXPECT_EQ("", UndefVar);

Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
UndefVar = Substitution.getUndefVarName();
EXPECT_EQ("", UndefVar);
}

TEST_F(FileCheckTest, FileCheckContext) {
FileCheckPatternContext Cxt = FileCheckPatternContext();
std::vector<std::string> GlobalDefines;
Expand Down Expand Up @@ -176,9 +222,9 @@ TEST_F(FileCheckTest, FileCheckContext) {
StringRef LocalVarStr = "LocalVar";
StringRef EmptyVarStr = "EmptyVar";
StringRef UnknownVarStr = "UnknownVar";
llvm::Optional<StringRef> LocalVar = Cxt.getVarValue(LocalVarStr);
llvm::Optional<StringRef> EmptyVar = Cxt.getVarValue(EmptyVarStr);
llvm::Optional<StringRef> UnknownVar = Cxt.getVarValue(UnknownVarStr);
llvm::Optional<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
llvm::Optional<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
llvm::Optional<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
EXPECT_TRUE(LocalVar);
EXPECT_EQ(*LocalVar, "FOO");
EXPECT_TRUE(EmptyVar);
Expand All @@ -187,23 +233,23 @@ TEST_F(FileCheckTest, FileCheckContext) {

// Clear local variables and check they become absent.
Cxt.clearLocalVars();
LocalVar = Cxt.getVarValue(LocalVarStr);
LocalVar = Cxt.getPatternVarValue(LocalVarStr);
EXPECT_FALSE(LocalVar);
EmptyVar = Cxt.getVarValue(EmptyVarStr);
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
EXPECT_FALSE(EmptyVar);

// Redefine global variables and check variables are defined again.
GlobalDefines.emplace_back(std::string("$GlobalVar=BAR"));
GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
EXPECT_FALSE(GotError);
StringRef GlobalVarStr = "$GlobalVar";
llvm::Optional<StringRef> GlobalVar = Cxt.getVarValue(GlobalVarStr);
llvm::Optional<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
EXPECT_TRUE(GlobalVar);
EXPECT_EQ(*GlobalVar, "BAR");

// Clear local variables and check global variables remain defined.
Cxt.clearLocalVars();
GlobalVar = Cxt.getVarValue(GlobalVarStr);
GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
EXPECT_TRUE(GlobalVar);
}
} // namespace