Skip to content

Commit e038fa7

Browse files
author
Thomas Preud'homme
committedApr 15, 2019
FileCheck [1/12]: Move variable table in new object
Summary: This patch is part of a patch series to add support for FileCheck numeric expressions. This specific patch adds a new class to hold pattern matching global state. The table holding the values of FileCheck variable constitutes some sort of global state for the matching phase, yet is passed as parameters of all functions using it. This commit create a new FileCheckPatternContext class pointed at from FileCheckPattern. While it increases the line count, it separates local data from global state. Later commits build on that to add numeric expression global state to that class. 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/D60381 llvm-svn: 358390
1 parent 301ed1c commit e038fa7

File tree

4 files changed

+186
-101
lines changed

4 files changed

+186
-101
lines changed
 

‎llvm/include/llvm/Support/FileCheck.h

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,37 @@ class FileCheckType {
7979

8080
std::string getDescription(StringRef Prefix) const;
8181
};
82-
}
82+
} // namespace Check
8383

8484
struct FileCheckDiag;
8585

86+
/// Class holding the FileCheckPattern global state, shared by all patterns:
87+
/// tables holding values of variables and whether they are defined or not at
88+
/// any given time in the matching process.
89+
class FileCheckPatternContext {
90+
friend class FileCheckPattern;
91+
92+
private:
93+
/// When matching a given pattern, this holds the value of all the FileCheck
94+
/// variables defined in previous patterns. In a pattern only the last
95+
/// definition for a given variable is recorded in this table, back-references
96+
/// are used for uses after any the other definition.
97+
StringMap<StringRef> GlobalVariableTable;
98+
99+
public:
100+
/// Return the value of variable \p VarName or None if no such variable has
101+
/// been defined.
102+
llvm::Optional<StringRef> getVarValue(StringRef VarName);
103+
104+
/// Define variables from definitions given on the command line passed as a
105+
/// vector of VAR=VAL strings in \p CmdlineDefines.
106+
void defineCmdlineVariables(std::vector<std::string> &CmdlineDefines);
107+
108+
/// Undefine local variables (variables whose name does not start with a '$'
109+
/// sign), i.e. remove them from GlobalVariableTable.
110+
void clearLocalVars();
111+
};
112+
86113
class FileCheckPattern {
87114
SMLoc PatternLoc;
88115

@@ -106,27 +133,34 @@ class FileCheckPattern {
106133
/// 1.
107134
std::map<StringRef, unsigned> VariableDefs;
108135

136+
/// Pointer to the class instance shared by all patterns holding a table with
137+
/// the values of live variables at the start of any given CHECK line.
138+
FileCheckPatternContext *Context;
139+
109140
Check::FileCheckType CheckTy;
110141

111142
/// Contains the number of line this pattern is in.
112143
unsigned LineNumber;
113144

114145
public:
115-
explicit FileCheckPattern(Check::FileCheckType Ty)
116-
: CheckTy(Ty) {}
146+
explicit FileCheckPattern(Check::FileCheckType Ty,
147+
FileCheckPatternContext *Context)
148+
: Context(Context), CheckTy(Ty) {}
117149

118150
/// Returns the location in source code.
119151
SMLoc getLoc() const { return PatternLoc; }
120152

153+
/// Returns the pointer to the global state for all patterns in this
154+
/// FileCheck instance.
155+
FileCheckPatternContext *getContext() const { return Context; }
121156
bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM,
122157
unsigned LineNumber, const FileCheckRequest &Req);
123-
size_t Match(StringRef Buffer, size_t &MatchLen,
124-
StringMap<StringRef> &VariableTable) const;
125-
void PrintVariableUses(const SourceMgr &SM, StringRef Buffer,
126-
const StringMap<StringRef> &VariableTable,
158+
size_t match(StringRef Buffer, size_t &MatchLen) const;
159+
/// Print value of successful substitutions or name of undefined pattern
160+
/// variables preventing such a successful substitution.
161+
void printVariableUses(const SourceMgr &SM, StringRef Buffer,
127162
SMRange MatchRange = None) const;
128-
void PrintFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
129-
const StringMap<StringRef> &VariableTable,
163+
void printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
130164
std::vector<FileCheckDiag> *Diags) const;
131165

132166
bool hasVariable() const {
@@ -140,9 +174,7 @@ class FileCheckPattern {
140174
private:
141175
bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM);
142176
void AddBackrefToRegEx(unsigned BackrefNum);
143-
unsigned
144-
ComputeMatchDistance(StringRef Buffer,
145-
const StringMap<StringRef> &VariableTable) const;
177+
unsigned computeMatchDistance(StringRef Buffer) const;
146178
bool EvaluateExpression(StringRef Expr, std::string &Value) const;
147179
size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
148180
};
@@ -223,19 +255,17 @@ struct FileCheckString {
223255
: Pat(P), Prefix(S), Loc(L) {}
224256

225257
size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode,
226-
size_t &MatchLen, StringMap<StringRef> &VariableTable,
227-
FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const;
258+
size_t &MatchLen, FileCheckRequest &Req,
259+
std::vector<FileCheckDiag> *Diags) const;
228260

229261
bool CheckNext(const SourceMgr &SM, StringRef Buffer) const;
230262
bool CheckSame(const SourceMgr &SM, StringRef Buffer) const;
231263
bool CheckNot(const SourceMgr &SM, StringRef Buffer,
232264
const std::vector<const FileCheckPattern *> &NotStrings,
233-
StringMap<StringRef> &VariableTable,
234265
const FileCheckRequest &Req,
235266
std::vector<FileCheckDiag> *Diags) const;
236267
size_t CheckDag(const SourceMgr &SM, StringRef Buffer,
237268
std::vector<const FileCheckPattern *> &NotStrings,
238-
StringMap<StringRef> &VariableTable,
239269
const FileCheckRequest &Req,
240270
std::vector<FileCheckDiag> *Diags) const;
241271
};
@@ -244,6 +274,7 @@ struct FileCheckString {
244274
/// use information from the request.
245275
class FileCheck {
246276
FileCheckRequest Req;
277+
FileCheckPatternContext PatternContext;
247278

248279
public:
249280
FileCheck(FileCheckRequest Req) : Req(Req) {}

‎llvm/lib/Support/FileCheck.cpp

Lines changed: 86 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,10 @@ bool FileCheckPattern::EvaluateExpression(StringRef Expr, std::string &Value) co
269269
/// there is a match, the size of the matched string is returned in \p
270270
/// MatchLen.
271271
///
272-
/// The \p VariableTable StringMap provides the current values of filecheck
273-
/// variables and is updated if this match defines new values.
274-
size_t FileCheckPattern::Match(StringRef Buffer, size_t &MatchLen,
275-
StringMap<StringRef> &VariableTable) const {
272+
/// The GlobalVariableTable StringMap in the FileCheckPatternContext class
273+
/// instance provides the current values of FileCheck variables and is updated
274+
/// if this match defines new values.
275+
size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
276276
// If this is the EOF pattern, match it immediately.
277277
if (CheckTy == Check::CheckEOF) {
278278
MatchLen = 0;
@@ -302,14 +302,14 @@ size_t FileCheckPattern::Match(StringRef Buffer, size_t &MatchLen,
302302
if (!EvaluateExpression(VariableUse.first, Value))
303303
return StringRef::npos;
304304
} else {
305-
StringMap<StringRef>::iterator it =
306-
VariableTable.find(VariableUse.first);
305+
llvm::Optional<StringRef> ValueRef =
306+
Context->getVarValue(VariableUse.first);
307307
// If the variable is undefined, return an error.
308-
if (it == VariableTable.end())
308+
if (!ValueRef)
309309
return StringRef::npos;
310310

311311
// Look up the value and escape it so that we can put it into the regex.
312-
Value += Regex::escape(it->second);
312+
Value += Regex::escape(*ValueRef);
313313
}
314314

315315
// Plop it into the regex at the adjusted offset.
@@ -333,7 +333,8 @@ size_t FileCheckPattern::Match(StringRef Buffer, size_t &MatchLen,
333333
// If this defines any variables, remember their values.
334334
for (const auto &VariableDef : VariableDefs) {
335335
assert(VariableDef.second < MatchInfo.size() && "Internal paren error");
336-
VariableTable[VariableDef.first] = MatchInfo[VariableDef.second];
336+
Context->GlobalVariableTable[VariableDef.first] =
337+
MatchInfo[VariableDef.second];
337338
}
338339

339340
// Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
@@ -344,13 +345,10 @@ size_t FileCheckPattern::Match(StringRef Buffer, size_t &MatchLen,
344345
return FullMatch.data() - Buffer.data() + MatchStartSkip;
345346
}
346347

347-
348348
/// Computes an arbitrary estimate for the quality of matching this pattern at
349349
/// the start of \p Buffer; a distance of zero should correspond to a perfect
350350
/// match.
351-
unsigned
352-
FileCheckPattern::ComputeMatchDistance(StringRef Buffer,
353-
const StringMap<StringRef> &VariableTable) const {
351+
unsigned FileCheckPattern::computeMatchDistance(StringRef Buffer) const {
354352
// Just compute the number of matching characters. For regular expressions, we
355353
// just compare against the regex itself and hope for the best.
356354
//
@@ -367,9 +365,8 @@ FileCheckPattern::ComputeMatchDistance(StringRef Buffer,
367365
return BufferPrefix.edit_distance(ExampleString);
368366
}
369367

370-
void FileCheckPattern::PrintVariableUses(const SourceMgr &SM, StringRef Buffer,
371-
const StringMap<StringRef> &VariableTable,
372-
SMRange MatchRange) const {
368+
void FileCheckPattern::printVariableUses(const SourceMgr &SM, StringRef Buffer,
369+
SMRange MatchRange) const {
373370
// If this was a regular expression using variables, print the current
374371
// variable values.
375372
if (!VariableUses.empty()) {
@@ -388,16 +385,16 @@ void FileCheckPattern::PrintVariableUses(const SourceMgr &SM, StringRef Buffer,
388385
OS.write_escaped(Var) << "\"";
389386
}
390387
} else {
391-
StringMap<StringRef>::const_iterator it = VariableTable.find(Var);
388+
llvm::Optional<StringRef> VarValue = Context->getVarValue(Var);
392389

393390
// Check for undefined variable references.
394-
if (it == VariableTable.end()) {
391+
if (!VarValue) {
395392
OS << "uses undefined variable \"";
396393
OS.write_escaped(Var) << "\"";
397394
} else {
398395
OS << "with variable \"";
399396
OS.write_escaped(Var) << "\" equal to \"";
400-
OS.write_escaped(it->second) << "\"";
397+
OS.write_escaped(*VarValue) << "\"";
401398
}
402399
}
403400

@@ -429,9 +426,8 @@ static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,
429426
return Range;
430427
}
431428

432-
void FileCheckPattern::PrintFuzzyMatch(
429+
void FileCheckPattern::printFuzzyMatch(
433430
const SourceMgr &SM, StringRef Buffer,
434-
const StringMap<StringRef> &VariableTable,
435431
std::vector<FileCheckDiag> *Diags) const {
436432
// Attempt to find the closest/best fuzzy match. Usually an error happens
437433
// because some string in the output didn't exactly match. In these cases, we
@@ -453,7 +449,7 @@ void FileCheckPattern::PrintFuzzyMatch(
453449

454450
// Compute the "quality" of this match as an arbitrary combination of the
455451
// match distance and the number of lines skipped to get to this match.
456-
unsigned Distance = ComputeMatchDistance(Buffer.substr(i), VariableTable);
452+
unsigned Distance = computeMatchDistance(Buffer.substr(i));
457453
double Quality = Distance + (NumLinesForward / 100.);
458454

459455
if (Quality < BestQuality || Best == StringRef::npos) {
@@ -477,6 +473,15 @@ void FileCheckPattern::PrintFuzzyMatch(
477473
}
478474
}
479475

476+
llvm::Optional<StringRef>
477+
FileCheckPatternContext::getVarValue(StringRef VarName) {
478+
auto VarIter = GlobalVariableTable.find(VarName);
479+
if (VarIter == GlobalVariableTable.end())
480+
return llvm::None;
481+
482+
return VarIter->second;
483+
}
484+
480485
/// Finds the closing sequence of a regex variable usage or definition.
481486
///
482487
/// \p Str has to point in the beginning of the definition (right after the
@@ -747,9 +752,11 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
747752
///
748753
/// The strings are added to the CheckStrings vector. Returns true in case of
749754
/// an error, false otherwise.
750-
bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer,
751-
Regex &PrefixRE,
752-
std::vector<FileCheckString> &CheckStrings) {
755+
bool llvm::FileCheck::ReadCheckFile(
756+
SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
757+
std::vector<FileCheckString> &CheckStrings) {
758+
PatternContext.defineCmdlineVariables(Req.GlobalDefines);
759+
753760
std::vector<FileCheckPattern> ImplicitNegativeChecks;
754761
for (const auto &PatternString : Req.ImplicitCheckNot) {
755762
// Create a buffer with fake command line content in order to display the
@@ -763,7 +770,8 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer,
763770
CmdLine->getBuffer().substr(Prefix.size(), PatternString.size());
764771
SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
765772

766-
ImplicitNegativeChecks.push_back(FileCheckPattern(Check::CheckNot));
773+
ImplicitNegativeChecks.push_back(
774+
FileCheckPattern(Check::CheckNot, &PatternContext));
767775
ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer,
768776
"IMPLICIT-CHECK", SM, 0, Req);
769777
}
@@ -826,7 +834,7 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer,
826834
SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
827835

828836
// Parse the pattern.
829-
FileCheckPattern P(CheckTy);
837+
FileCheckPattern P(CheckTy, &PatternContext);
830838
if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber, Req))
831839
return true;
832840

@@ -870,8 +878,9 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer,
870878
// Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first
871879
// prefix as a filler for the error message.
872880
if (!DagNotMatches.empty()) {
873-
CheckStrings.emplace_back(FileCheckPattern(Check::CheckEOF), *Req.CheckPrefixes.begin(),
874-
SMLoc::getFromPointer(Buffer.data()));
881+
CheckStrings.emplace_back(
882+
FileCheckPattern(Check::CheckEOF, &PatternContext),
883+
*Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
875884
std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
876885
}
877886

@@ -896,8 +905,7 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer,
896905

897906
static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
898907
StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat,
899-
int MatchedCount, StringRef Buffer,
900-
StringMap<StringRef> &VariableTable, size_t MatchPos,
908+
int MatchedCount, StringRef Buffer, size_t MatchPos,
901909
size_t MatchLen, const FileCheckRequest &Req,
902910
std::vector<FileCheckDiag> *Diags) {
903911
bool PrintDiag = true;
@@ -929,24 +937,22 @@ static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
929937
Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
930938
SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
931939
{MatchRange});
932-
Pat.PrintVariableUses(SM, Buffer, VariableTable, MatchRange);
940+
Pat.printVariableUses(SM, Buffer, MatchRange);
933941
}
934942

935943
static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
936944
const FileCheckString &CheckStr, int MatchedCount,
937-
StringRef Buffer, StringMap<StringRef> &VariableTable,
938-
size_t MatchPos, size_t MatchLen, FileCheckRequest &Req,
945+
StringRef Buffer, size_t MatchPos, size_t MatchLen,
946+
FileCheckRequest &Req,
939947
std::vector<FileCheckDiag> *Diags) {
940948
PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
941-
MatchedCount, Buffer, VariableTable, MatchPos, MatchLen, Req,
942-
Diags);
949+
MatchedCount, Buffer, MatchPos, MatchLen, Req, Diags);
943950
}
944951

945952
static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
946953
StringRef Prefix, SMLoc Loc,
947954
const FileCheckPattern &Pat, int MatchedCount,
948-
StringRef Buffer, StringMap<StringRef> &VariableTable,
949-
bool VerboseVerbose,
955+
StringRef Buffer, bool VerboseVerbose,
950956
std::vector<FileCheckDiag> *Diags) {
951957
bool PrintDiag = true;
952958
if (!ExpectedMatch) {
@@ -982,19 +988,18 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
982988
SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here");
983989

984990
// Allow the pattern to print additional information if desired.
985-
Pat.PrintVariableUses(SM, Buffer, VariableTable);
991+
Pat.printVariableUses(SM, Buffer);
986992

987993
if (ExpectedMatch)
988-
Pat.PrintFuzzyMatch(SM, Buffer, VariableTable, Diags);
994+
Pat.printFuzzyMatch(SM, Buffer, Diags);
989995
}
990996

991997
static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
992998
const FileCheckString &CheckStr, int MatchedCount,
993-
StringRef Buffer, StringMap<StringRef> &VariableTable,
994-
bool VerboseVerbose,
999+
StringRef Buffer, bool VerboseVerbose,
9951000
std::vector<FileCheckDiag> *Diags) {
9961001
PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
997-
MatchedCount, Buffer, VariableTable, VerboseVerbose, Diags);
1002+
MatchedCount, Buffer, VerboseVerbose, Diags);
9981003
}
9991004

10001005
/// Count the number of newlines in the specified range.
@@ -1023,7 +1028,6 @@ static unsigned CountNumNewlinesBetween(StringRef Range,
10231028
/// Match check string and its "not strings" and/or "dag strings".
10241029
size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
10251030
bool IsLabelScanMode, size_t &MatchLen,
1026-
StringMap<StringRef> &VariableTable,
10271031
FileCheckRequest &Req,
10281032
std::vector<FileCheckDiag> *Diags) const {
10291033
size_t LastPos = 0;
@@ -1035,7 +1039,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
10351039
// over the block again (including the last CHECK-LABEL) in normal mode.
10361040
if (!IsLabelScanMode) {
10371041
// Match "dag strings" (with mixed "not strings" if any).
1038-
LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable, Req, Diags);
1042+
LastPos = CheckDag(SM, Buffer, NotStrings, Req, Diags);
10391043
if (LastPos == StringRef::npos)
10401044
return StringRef::npos;
10411045
}
@@ -1050,18 +1054,17 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
10501054
StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
10511055
size_t CurrentMatchLen;
10521056
// get a match at current start point
1053-
size_t MatchPos = Pat.Match(MatchBuffer, CurrentMatchLen, VariableTable);
1057+
size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen);
10541058
if (i == 1)
10551059
FirstMatchPos = LastPos + MatchPos;
10561060

10571061
// report
10581062
if (MatchPos == StringRef::npos) {
1059-
PrintNoMatch(true, SM, *this, i, MatchBuffer, VariableTable,
1060-
Req.VerboseVerbose, Diags);
1063+
PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags);
10611064
return StringRef::npos;
10621065
}
1063-
PrintMatch(true, SM, *this, i, MatchBuffer, VariableTable, MatchPos,
1064-
CurrentMatchLen, Req, Diags);
1066+
PrintMatch(true, SM, *this, i, MatchBuffer, MatchPos, CurrentMatchLen, Req,
1067+
Diags);
10651068

10661069
// move start point after the match
10671070
LastMatchEnd += MatchPos + CurrentMatchLen;
@@ -1096,7 +1099,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
10961099

10971100
// If this match had "not strings", verify that they don't exist in the
10981101
// skipped region.
1099-
if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags))
1102+
if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
11001103
return StringRef::npos;
11011104
}
11021105

@@ -1170,22 +1173,21 @@ bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const {
11701173
bool FileCheckString::CheckNot(
11711174
const SourceMgr &SM, StringRef Buffer,
11721175
const std::vector<const FileCheckPattern *> &NotStrings,
1173-
StringMap<StringRef> &VariableTable, const FileCheckRequest &Req,
1174-
std::vector<FileCheckDiag> *Diags) const {
1176+
const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const {
11751177
for (const FileCheckPattern *Pat : NotStrings) {
11761178
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
11771179

11781180
size_t MatchLen = 0;
1179-
size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable);
1181+
size_t Pos = Pat->match(Buffer, MatchLen);
11801182

11811183
if (Pos == StringRef::npos) {
11821184
PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
1183-
VariableTable, Req.VerboseVerbose, Diags);
1185+
Req.VerboseVerbose, Diags);
11841186
continue;
11851187
}
11861188

1187-
PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, VariableTable,
1188-
Pos, MatchLen, Req, Diags);
1189+
PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen,
1190+
Req, Diags);
11891191

11901192
return true;
11911193
}
@@ -1197,7 +1199,6 @@ bool FileCheckString::CheckNot(
11971199
size_t
11981200
FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
11991201
std::vector<const FileCheckPattern *> &NotStrings,
1200-
StringMap<StringRef> &VariableTable,
12011202
const FileCheckRequest &Req,
12021203
std::vector<FileCheckDiag> *Diags) const {
12031204
if (DagNotStrings.empty())
@@ -1238,19 +1239,19 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
12381239
// CHECK-DAG group.
12391240
for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
12401241
StringRef MatchBuffer = Buffer.substr(MatchPos);
1241-
size_t MatchPosBuf = Pat.Match(MatchBuffer, MatchLen, VariableTable);
1242+
size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen);
12421243
// With a group of CHECK-DAGs, a single mismatching means the match on
12431244
// that group of CHECK-DAGs fails immediately.
12441245
if (MatchPosBuf == StringRef::npos) {
12451246
PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer,
1246-
VariableTable, Req.VerboseVerbose, Diags);
1247+
Req.VerboseVerbose, Diags);
12471248
return StringRef::npos;
12481249
}
12491250
// Re-calc it as the offset relative to the start of the original string.
12501251
MatchPos += MatchPosBuf;
12511252
if (Req.VerboseVerbose)
1252-
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer,
1253-
VariableTable, MatchPos, MatchLen, Req, Diags);
1253+
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
1254+
MatchLen, Req, Diags);
12541255
MatchRange M{MatchPos, MatchPos + MatchLen};
12551256
if (Req.AllowDeprecatedDagOverlap) {
12561257
// We don't need to track all matches in this mode, so we just maintain
@@ -1297,8 +1298,8 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
12971298
MatchPos = MI->End;
12981299
}
12991300
if (!Req.VerboseVerbose)
1300-
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, VariableTable,
1301-
MatchPos, MatchLen, Req, Diags);
1301+
PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
1302+
MatchLen, Req, Diags);
13021303

13031304
// Handle the end of a CHECK-DAG group.
13041305
if (std::next(PatItr) == PatEnd ||
@@ -1309,7 +1310,7 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
13091310
// region.
13101311
StringRef SkippedRegion =
13111312
Buffer.slice(StartPos, MatchRanges.begin()->Pos);
1312-
if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags))
1313+
if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
13131314
return StringRef::npos;
13141315
// Clear "not strings".
13151316
NotStrings.clear();
@@ -1373,16 +1374,22 @@ Regex llvm::FileCheck::buildCheckPrefixRegex() {
13731374
return Regex(PrefixRegexStr);
13741375
}
13751376

1376-
// Remove local variables from \p VariableTable. Global variables
1377-
// (start with '$') are preserved.
1378-
static void ClearLocalVars(StringMap<StringRef> &VariableTable) {
1379-
SmallVector<StringRef, 16> LocalVars;
1380-
for (const auto &Var : VariableTable)
1377+
void FileCheckPatternContext::defineCmdlineVariables(
1378+
std::vector<std::string> &CmdlineDefines) {
1379+
assert(GlobalVariableTable.empty() &&
1380+
"Overriding defined variable with command-line variable definitions");
1381+
for (StringRef CmdlineDef : CmdlineDefines)
1382+
GlobalVariableTable.insert(CmdlineDef.split('='));
1383+
}
1384+
1385+
void FileCheckPatternContext::clearLocalVars() {
1386+
SmallVector<StringRef, 16> LocalPatternVars, LocalNumericVars;
1387+
for (const StringMapEntry<StringRef> &Var : GlobalVariableTable)
13811388
if (Var.first()[0] != '$')
1382-
LocalVars.push_back(Var.first());
1389+
LocalPatternVars.push_back(Var.first());
13831390

1384-
for (const auto &Var : LocalVars)
1385-
VariableTable.erase(Var);
1391+
for (const auto &Var : LocalPatternVars)
1392+
GlobalVariableTable.erase(Var);
13861393
}
13871394

13881395
/// Check the input to FileCheck provided in the \p Buffer against the \p
@@ -1394,12 +1401,6 @@ bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,
13941401
std::vector<FileCheckDiag> *Diags) {
13951402
bool ChecksFailed = false;
13961403

1397-
/// VariableTable - This holds all the current filecheck variables.
1398-
StringMap<StringRef> VariableTable;
1399-
1400-
for (const auto& Def : Req.GlobalDefines)
1401-
VariableTable.insert(StringRef(Def).split('='));
1402-
14031404
unsigned i = 0, j = 0, e = CheckStrings.size();
14041405
while (true) {
14051406
StringRef CheckRegion;
@@ -1414,10 +1415,10 @@ bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,
14141415

14151416
// Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG
14161417
size_t MatchLabelLen = 0;
1417-
size_t MatchLabelPos = CheckLabelStr.Check(
1418-
SM, Buffer, true, MatchLabelLen, VariableTable, Req, Diags);
1418+
size_t MatchLabelPos =
1419+
CheckLabelStr.Check(SM, Buffer, true, MatchLabelLen, Req, Diags);
14191420
if (MatchLabelPos == StringRef::npos)
1420-
// Immediately bail of CHECK-LABEL fails, nothing else we can do.
1421+
// Immediately bail if CHECK-LABEL fails, nothing else we can do.
14211422
return false;
14221423

14231424
CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen);
@@ -1426,16 +1427,16 @@ bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,
14261427
}
14271428

14281429
if (Req.EnableVarScope)
1429-
ClearLocalVars(VariableTable);
1430+
PatternContext.clearLocalVars();
14301431

14311432
for (; i != j; ++i) {
14321433
const FileCheckString &CheckStr = CheckStrings[i];
14331434

14341435
// Check each string within the scanned region, including a second check
14351436
// of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)
14361437
size_t MatchLen = 0;
1437-
size_t MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen,
1438-
VariableTable, Req, Diags);
1438+
size_t MatchPos =
1439+
CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);
14391440

14401441
if (MatchPos == StringRef::npos) {
14411442
ChecksFailed = true;

‎llvm/unittests/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_llvm_unittest(SupportTests
2828
ErrnoTest.cpp
2929
ErrorOrTest.cpp
3030
ErrorTest.cpp
31+
FileCheckTest.cpp
3132
FileOutputBufferTest.cpp
3233
FormatVariadicTest.cpp
3334
GlobPatternTest.cpp
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===- llvm/unittest/Support/FileCheckTest.cpp - FileCheck tests --===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Support/FileCheck.h"
10+
#include "gtest/gtest.h"
11+
12+
using namespace llvm;
13+
namespace {
14+
15+
class FileCheckTest : public ::testing::Test {};
16+
17+
TEST_F(FileCheckTest, FileCheckContext) {
18+
FileCheckPatternContext Cxt;
19+
std::vector<std::string> GlobalDefines;
20+
21+
// Define local and global variables from command-line.
22+
GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
23+
Cxt.defineCmdlineVariables(GlobalDefines);
24+
25+
// Check defined variables are present and undefined is absent.
26+
StringRef LocalVarStr = "LocalVar";
27+
StringRef UnknownVarStr = "UnknownVar";
28+
llvm::Optional<StringRef> LocalVar = Cxt.getVarValue(LocalVarStr);
29+
llvm::Optional<StringRef> UnknownVar = Cxt.getVarValue(UnknownVarStr);
30+
EXPECT_TRUE(LocalVar);
31+
EXPECT_EQ(*LocalVar, "FOO");
32+
EXPECT_FALSE(UnknownVar);
33+
34+
// Clear local variables and check they become absent.
35+
Cxt.clearLocalVars();
36+
LocalVar = Cxt.getVarValue(LocalVarStr);
37+
EXPECT_FALSE(LocalVar);
38+
39+
// Redefine global variables and check variables are defined again.
40+
GlobalDefines.emplace_back(std::string("$GlobalVar=BAR"));
41+
Cxt.defineCmdlineVariables(GlobalDefines);
42+
StringRef GlobalVarStr = "$GlobalVar";
43+
llvm::Optional<StringRef> GlobalVar = Cxt.getVarValue(GlobalVarStr);
44+
EXPECT_TRUE(GlobalVar);
45+
EXPECT_EQ(*GlobalVar, "BAR");
46+
47+
// Clear local variables and check global variables remain defined.
48+
Cxt.clearLocalVars();
49+
GlobalVar = Cxt.getVarValue(GlobalVarStr);
50+
EXPECT_TRUE(GlobalVar);
51+
}
52+
} // namespace

0 commit comments

Comments
 (0)
Please sign in to comment.