diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -79,10 +79,37 @@ std::string getDescription(StringRef Prefix) const; }; -} +} // namespace Check struct FileCheckDiag; +/// Class holding the FileCheckPattern global state, shared by all patterns: +/// tables holding values of variables and whether they are defined or not at +/// any given time in the matching process. +class FileCheckPatternContext { + friend class FileCheckPattern; + +private: + /// When matching a given pattern, this holds the value of all the FileCheck + /// variables defined in previous patterns. In a pattern only the last + /// definition for a given variable is recorded in this table, back-references + /// are used for uses after any the other definition. + StringMap GlobalVariableTable; + +public: + /// Return the value of variable \p VarName or None if no such variable has + /// been defined. + llvm::Optional getVarValue(StringRef VarName); + + /// Define variables from definitions given on the command line passed as a + /// vector of VAR=VAL strings in \p CmdlineDefines. + void defineCmdlineVariables(std::vector &CmdlineDefines); + + /// Undefine local variables (variables whose name does not start with a '$' + /// sign), i.e. remove them from GlobalVariableTable. + void clearLocalVars(); +}; + class FileCheckPattern { SMLoc PatternLoc; @@ -106,27 +133,34 @@ /// 1. std::map VariableDefs; + /// Pointer to the class instance shared by all patterns holding a table with + /// the values of live variables at the start of any given CHECK line. + FileCheckPatternContext *Context; + Check::FileCheckType CheckTy; /// Contains the number of line this pattern is in. unsigned LineNumber; public: - explicit FileCheckPattern(Check::FileCheckType Ty) - : CheckTy(Ty) {} + explicit FileCheckPattern(Check::FileCheckType Ty, + FileCheckPatternContext *Context) + : Context(Context), CheckTy(Ty) {} /// Returns the location in source code. SMLoc getLoc() const { return PatternLoc; } + /// Returns the pointer to the global state for all patterns in this + /// FileCheck instance. + FileCheckPatternContext *getContext() const { return Context; } bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, unsigned LineNumber, const FileCheckRequest &Req); - size_t Match(StringRef Buffer, size_t &MatchLen, - StringMap &VariableTable) const; - void PrintVariableUses(const SourceMgr &SM, StringRef Buffer, - const StringMap &VariableTable, + size_t match(StringRef Buffer, size_t &MatchLen) const; + /// Print value of successful substitutions or name of undefined pattern + /// variables preventing such a successful substitution. + void printVariableUses(const SourceMgr &SM, StringRef Buffer, SMRange MatchRange = None) const; - void PrintFuzzyMatch(const SourceMgr &SM, StringRef Buffer, - const StringMap &VariableTable, + void printFuzzyMatch(const SourceMgr &SM, StringRef Buffer, std::vector *Diags) const; bool hasVariable() const { @@ -140,9 +174,7 @@ private: bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM); void AddBackrefToRegEx(unsigned BackrefNum); - unsigned - ComputeMatchDistance(StringRef Buffer, - const StringMap &VariableTable) const; + unsigned computeMatchDistance(StringRef Buffer) const; bool EvaluateExpression(StringRef Expr, std::string &Value) const; size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM); }; @@ -223,19 +255,17 @@ : Pat(P), Prefix(S), Loc(L) {} size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode, - size_t &MatchLen, StringMap &VariableTable, - FileCheckRequest &Req, std::vector *Diags) const; + size_t &MatchLen, FileCheckRequest &Req, + std::vector *Diags) const; bool CheckNext(const SourceMgr &SM, StringRef Buffer) const; bool CheckSame(const SourceMgr &SM, StringRef Buffer) const; bool CheckNot(const SourceMgr &SM, StringRef Buffer, const std::vector &NotStrings, - StringMap &VariableTable, const FileCheckRequest &Req, std::vector *Diags) const; size_t CheckDag(const SourceMgr &SM, StringRef Buffer, std::vector &NotStrings, - StringMap &VariableTable, const FileCheckRequest &Req, std::vector *Diags) const; }; @@ -244,6 +274,7 @@ /// use information from the request. class FileCheck { FileCheckRequest Req; + FileCheckPatternContext PatternContext; public: FileCheck(FileCheckRequest Req) : Req(Req) {} diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -269,10 +269,10 @@ /// there is a match, the size of the matched string is returned in \p /// MatchLen. /// -/// The \p VariableTable StringMap provides the current values of filecheck -/// variables and is updated if this match defines new values. -size_t FileCheckPattern::Match(StringRef Buffer, size_t &MatchLen, - StringMap &VariableTable) const { +/// The GlobalVariableTable StringMap in the FileCheckPatternContext class +/// instance provides the current values of FileCheck 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) { MatchLen = 0; @@ -302,14 +302,14 @@ if (!EvaluateExpression(VariableUse.first, Value)) return StringRef::npos; } else { - StringMap::iterator it = - VariableTable.find(VariableUse.first); + llvm::Optional ValueRef = + Context->getVarValue(VariableUse.first); // If the variable is undefined, return an error. - if (it == VariableTable.end()) + if (!ValueRef) return StringRef::npos; // Look up the value and escape it so that we can put it into the regex. - Value += Regex::escape(it->second); + Value += Regex::escape(*ValueRef); } // Plop it into the regex at the adjusted offset. @@ -333,7 +333,8 @@ // If this defines any variables, remember their values. for (const auto &VariableDef : VariableDefs) { assert(VariableDef.second < MatchInfo.size() && "Internal paren error"); - VariableTable[VariableDef.first] = MatchInfo[VariableDef.second]; + Context->GlobalVariableTable[VariableDef.first] = + MatchInfo[VariableDef.second]; } // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after @@ -344,13 +345,10 @@ return FullMatch.data() - Buffer.data() + MatchStartSkip; } - /// Computes an arbitrary estimate for the quality of matching this pattern at /// the start of \p Buffer; a distance of zero should correspond to a perfect /// match. -unsigned -FileCheckPattern::ComputeMatchDistance(StringRef Buffer, - const StringMap &VariableTable) const { +unsigned FileCheckPattern::computeMatchDistance(StringRef Buffer) const { // Just compute the number of matching characters. For regular expressions, we // just compare against the regex itself and hope for the best. // @@ -367,9 +365,8 @@ return BufferPrefix.edit_distance(ExampleString); } -void FileCheckPattern::PrintVariableUses(const SourceMgr &SM, StringRef Buffer, - const StringMap &VariableTable, - SMRange MatchRange) const { +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()) { @@ -388,16 +385,16 @@ OS.write_escaped(Var) << "\""; } } else { - StringMap::const_iterator it = VariableTable.find(Var); + llvm::Optional VarValue = Context->getVarValue(Var); // Check for undefined variable references. - if (it == VariableTable.end()) { + if (!VarValue) { OS << "uses undefined variable \""; OS.write_escaped(Var) << "\""; } else { OS << "with variable \""; OS.write_escaped(Var) << "\" equal to \""; - OS.write_escaped(it->second) << "\""; + OS.write_escaped(*VarValue) << "\""; } } @@ -429,9 +426,8 @@ return Range; } -void FileCheckPattern::PrintFuzzyMatch( +void FileCheckPattern::printFuzzyMatch( const SourceMgr &SM, StringRef Buffer, - const StringMap &VariableTable, std::vector *Diags) const { // Attempt to find the closest/best fuzzy match. Usually an error happens // because some string in the output didn't exactly match. In these cases, we @@ -453,7 +449,7 @@ // Compute the "quality" of this match as an arbitrary combination of the // match distance and the number of lines skipped to get to this match. - unsigned Distance = ComputeMatchDistance(Buffer.substr(i), VariableTable); + unsigned Distance = computeMatchDistance(Buffer.substr(i)); double Quality = Distance + (NumLinesForward / 100.); if (Quality < BestQuality || Best == StringRef::npos) { @@ -477,6 +473,15 @@ } } +llvm::Optional +FileCheckPatternContext::getVarValue(StringRef VarName) { + auto VarIter = GlobalVariableTable.find(VarName); + if (VarIter == GlobalVariableTable.end()) + return llvm::None; + + return VarIter->second; +} + /// 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 @@ -747,9 +752,11 @@ /// /// The strings are added to the CheckStrings vector. Returns true in case of /// an error, false otherwise. -bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, - Regex &PrefixRE, - std::vector &CheckStrings) { +bool llvm::FileCheck::ReadCheckFile( + SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, + std::vector &CheckStrings) { + PatternContext.defineCmdlineVariables(Req.GlobalDefines); + std::vector ImplicitNegativeChecks; for (const auto &PatternString : Req.ImplicitCheckNot) { // Create a buffer with fake command line content in order to display the @@ -763,7 +770,8 @@ CmdLine->getBuffer().substr(Prefix.size(), PatternString.size()); SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); - ImplicitNegativeChecks.push_back(FileCheckPattern(Check::CheckNot)); + ImplicitNegativeChecks.push_back( + FileCheckPattern(Check::CheckNot, &PatternContext)); ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer, "IMPLICIT-CHECK", SM, 0, Req); } @@ -826,7 +834,7 @@ SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); // Parse the pattern. - FileCheckPattern P(CheckTy); + FileCheckPattern P(CheckTy, &PatternContext); if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber, Req)) return true; @@ -870,8 +878,9 @@ // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first // prefix as a filler for the error message. if (!DagNotMatches.empty()) { - CheckStrings.emplace_back(FileCheckPattern(Check::CheckEOF), *Req.CheckPrefixes.begin(), - SMLoc::getFromPointer(Buffer.data())); + CheckStrings.emplace_back( + FileCheckPattern(Check::CheckEOF, &PatternContext), + *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data())); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } @@ -896,8 +905,7 @@ static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat, - int MatchedCount, StringRef Buffer, - StringMap &VariableTable, size_t MatchPos, + int MatchedCount, StringRef Buffer, size_t MatchPos, size_t MatchLen, const FileCheckRequest &Req, std::vector *Diags) { bool PrintDiag = true; @@ -929,24 +937,22 @@ Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message); SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here", {MatchRange}); - Pat.PrintVariableUses(SM, Buffer, VariableTable, MatchRange); + Pat.printVariableUses(SM, Buffer, MatchRange); } static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, const FileCheckString &CheckStr, int MatchedCount, - StringRef Buffer, StringMap &VariableTable, - size_t MatchPos, size_t MatchLen, FileCheckRequest &Req, + StringRef Buffer, size_t MatchPos, size_t MatchLen, + FileCheckRequest &Req, std::vector *Diags) { PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, - MatchedCount, Buffer, VariableTable, MatchPos, MatchLen, Req, - Diags); + MatchedCount, Buffer, MatchPos, MatchLen, Req, Diags); } static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat, int MatchedCount, - StringRef Buffer, StringMap &VariableTable, - bool VerboseVerbose, + StringRef Buffer, bool VerboseVerbose, std::vector *Diags) { bool PrintDiag = true; if (!ExpectedMatch) { @@ -982,19 +988,18 @@ SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here"); // Allow the pattern to print additional information if desired. - Pat.PrintVariableUses(SM, Buffer, VariableTable); + Pat.printVariableUses(SM, Buffer); if (ExpectedMatch) - Pat.PrintFuzzyMatch(SM, Buffer, VariableTable, Diags); + Pat.printFuzzyMatch(SM, Buffer, Diags); } static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, const FileCheckString &CheckStr, int MatchedCount, - StringRef Buffer, StringMap &VariableTable, - bool VerboseVerbose, + StringRef Buffer, bool VerboseVerbose, std::vector *Diags) { PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, - MatchedCount, Buffer, VariableTable, VerboseVerbose, Diags); + MatchedCount, Buffer, VerboseVerbose, Diags); } /// Count the number of newlines in the specified range. @@ -1023,7 +1028,6 @@ /// Match check string and its "not strings" and/or "dag strings". size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode, size_t &MatchLen, - StringMap &VariableTable, FileCheckRequest &Req, std::vector *Diags) const { size_t LastPos = 0; @@ -1035,7 +1039,7 @@ // over the block again (including the last CHECK-LABEL) in normal mode. if (!IsLabelScanMode) { // Match "dag strings" (with mixed "not strings" if any). - LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable, Req, Diags); + LastPos = CheckDag(SM, Buffer, NotStrings, Req, Diags); if (LastPos == StringRef::npos) return StringRef::npos; } @@ -1050,18 +1054,17 @@ StringRef MatchBuffer = Buffer.substr(LastMatchEnd); size_t CurrentMatchLen; // get a match at current start point - size_t MatchPos = Pat.Match(MatchBuffer, CurrentMatchLen, VariableTable); + size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen); if (i == 1) FirstMatchPos = LastPos + MatchPos; // report if (MatchPos == StringRef::npos) { - PrintNoMatch(true, SM, *this, i, MatchBuffer, VariableTable, - Req.VerboseVerbose, Diags); + PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags); return StringRef::npos; } - PrintMatch(true, SM, *this, i, MatchBuffer, VariableTable, MatchPos, - CurrentMatchLen, Req, Diags); + PrintMatch(true, SM, *this, i, MatchBuffer, MatchPos, CurrentMatchLen, Req, + Diags); // move start point after the match LastMatchEnd += MatchPos + CurrentMatchLen; @@ -1096,7 +1099,7 @@ // If this match had "not strings", verify that they don't exist in the // skipped region. - if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags)) + if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags)) return StringRef::npos; } @@ -1170,22 +1173,21 @@ bool FileCheckString::CheckNot( const SourceMgr &SM, StringRef Buffer, const std::vector &NotStrings, - StringMap &VariableTable, const FileCheckRequest &Req, - std::vector *Diags) const { + const FileCheckRequest &Req, std::vector *Diags) const { for (const FileCheckPattern *Pat : NotStrings) { assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); size_t MatchLen = 0; - size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable); + size_t Pos = Pat->match(Buffer, MatchLen); if (Pos == StringRef::npos) { PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, - VariableTable, Req.VerboseVerbose, Diags); + Req.VerboseVerbose, Diags); continue; } - PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, VariableTable, - Pos, MatchLen, Req, Diags); + PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen, + Req, Diags); return true; } @@ -1197,7 +1199,6 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, std::vector &NotStrings, - StringMap &VariableTable, const FileCheckRequest &Req, std::vector *Diags) const { if (DagNotStrings.empty()) @@ -1238,19 +1239,19 @@ // CHECK-DAG group. for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) { StringRef MatchBuffer = Buffer.substr(MatchPos); - size_t MatchPosBuf = Pat.Match(MatchBuffer, MatchLen, VariableTable); + size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen); // With a group of CHECK-DAGs, a single mismatching means the match on // that group of CHECK-DAGs fails immediately. if (MatchPosBuf == StringRef::npos) { PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer, - VariableTable, Req.VerboseVerbose, Diags); + Req.VerboseVerbose, Diags); return StringRef::npos; } // Re-calc it as the offset relative to the start of the original string. MatchPos += MatchPosBuf; if (Req.VerboseVerbose) - PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, - VariableTable, MatchPos, MatchLen, Req, Diags); + PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos, + MatchLen, Req, Diags); MatchRange M{MatchPos, MatchPos + MatchLen}; if (Req.AllowDeprecatedDagOverlap) { // We don't need to track all matches in this mode, so we just maintain @@ -1297,8 +1298,8 @@ MatchPos = MI->End; } if (!Req.VerboseVerbose) - PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, VariableTable, - MatchPos, MatchLen, Req, Diags); + PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos, + MatchLen, Req, Diags); // Handle the end of a CHECK-DAG group. if (std::next(PatItr) == PatEnd || @@ -1309,7 +1310,7 @@ // region. StringRef SkippedRegion = Buffer.slice(StartPos, MatchRanges.begin()->Pos); - if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags)) + if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags)) return StringRef::npos; // Clear "not strings". NotStrings.clear(); @@ -1373,16 +1374,22 @@ return Regex(PrefixRegexStr); } -// Remove local variables from \p VariableTable. Global variables -// (start with '$') are preserved. -static void ClearLocalVars(StringMap &VariableTable) { - SmallVector LocalVars; - for (const auto &Var : VariableTable) +void FileCheckPatternContext::defineCmdlineVariables( + std::vector &CmdlineDefines) { + assert(GlobalVariableTable.empty() && + "Overriding defined variable with command-line variable definitions"); + for (StringRef CmdlineDef : CmdlineDefines) + GlobalVariableTable.insert(CmdlineDef.split('=')); +} + +void FileCheckPatternContext::clearLocalVars() { + SmallVector LocalPatternVars, LocalNumericVars; + for (const StringMapEntry &Var : GlobalVariableTable) if (Var.first()[0] != '$') - LocalVars.push_back(Var.first()); + LocalPatternVars.push_back(Var.first()); - for (const auto &Var : LocalVars) - VariableTable.erase(Var); + for (const auto &Var : LocalPatternVars) + GlobalVariableTable.erase(Var); } /// Check the input to FileCheck provided in the \p Buffer against the \p @@ -1394,12 +1401,6 @@ std::vector *Diags) { bool ChecksFailed = false; - /// VariableTable - This holds all the current filecheck variables. - StringMap VariableTable; - - for (const auto& Def : Req.GlobalDefines) - VariableTable.insert(StringRef(Def).split('=')); - unsigned i = 0, j = 0, e = CheckStrings.size(); while (true) { StringRef CheckRegion; @@ -1414,10 +1415,10 @@ // Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG size_t MatchLabelLen = 0; - size_t MatchLabelPos = CheckLabelStr.Check( - SM, Buffer, true, MatchLabelLen, VariableTable, Req, Diags); + size_t MatchLabelPos = + CheckLabelStr.Check(SM, Buffer, true, MatchLabelLen, Req, Diags); if (MatchLabelPos == StringRef::npos) - // Immediately bail of CHECK-LABEL fails, nothing else we can do. + // Immediately bail if CHECK-LABEL fails, nothing else we can do. return false; CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen); @@ -1426,7 +1427,7 @@ } if (Req.EnableVarScope) - ClearLocalVars(VariableTable); + PatternContext.clearLocalVars(); for (; i != j; ++i) { const FileCheckString &CheckStr = CheckStrings[i]; @@ -1434,8 +1435,8 @@ // Check each string within the scanned region, including a second check // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG) size_t MatchLen = 0; - size_t MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen, - VariableTable, Req, Diags); + size_t MatchPos = + CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags); if (MatchPos == StringRef::npos) { ChecksFailed = true; diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -27,6 +27,7 @@ ErrnoTest.cpp ErrorOrTest.cpp ErrorTest.cpp + FileCheckTest.cpp FileOutputBufferTest.cpp FormatVariadicTest.cpp GlobPatternTest.cpp diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -0,0 +1,52 @@ +//===- 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" + +using namespace llvm; +namespace { + +class FileCheckTest : public ::testing::Test {}; + +TEST_F(FileCheckTest, FileCheckContext) { + FileCheckPatternContext Cxt; + std::vector GlobalDefines; + + // Define local and global variables from command-line. + GlobalDefines.emplace_back(std::string("LocalVar=FOO")); + Cxt.defineCmdlineVariables(GlobalDefines); + + // Check defined variables are present and undefined is absent. + StringRef LocalVarStr = "LocalVar"; + StringRef UnknownVarStr = "UnknownVar"; + llvm::Optional LocalVar = Cxt.getVarValue(LocalVarStr); + llvm::Optional UnknownVar = Cxt.getVarValue(UnknownVarStr); + EXPECT_TRUE(LocalVar); + EXPECT_EQ(*LocalVar, "FOO"); + EXPECT_FALSE(UnknownVar); + + // Clear local variables and check they become absent. + Cxt.clearLocalVars(); + LocalVar = Cxt.getVarValue(LocalVarStr); + EXPECT_FALSE(LocalVar); + + // Redefine global variables and check variables are defined again. + GlobalDefines.emplace_back(std::string("$GlobalVar=BAR")); + Cxt.defineCmdlineVariables(GlobalDefines); + StringRef GlobalVarStr = "$GlobalVar"; + llvm::Optional GlobalVar = Cxt.getVarValue(GlobalVarStr); + EXPECT_TRUE(GlobalVar); + EXPECT_EQ(*GlobalVar, "BAR"); + + // Clear local variables and check global variables remains defined. + Cxt.clearLocalVars(); + GlobalVar = Cxt.getVarValue(GlobalVarStr); + EXPECT_TRUE(GlobalVar); +} +} // namespace