llvm.org GIT mirror llvm / 3abee1b
FileCheck [4/12]: Introduce @LINE numeric expressions Summary: This patch is part of a patch series to add support for FileCheck numeric expressions. This specific patch introduces the @LINE numeric expressions. This commit introduces a new syntax to express a relation a numeric value in the input text must have with the line number of a given CHECK pattern: [[#<@LINE numeric expression>]]. Further commits build on that to express relations between several numeric values in the input text. To help with naming, regular variables are renamed into pattern variables and old @LINE expression syntax is referred to as legacy numeric expression. Compared to existing @LINE expressions, this new syntax allow arbitrary spacing between the component of the expression. It offers otherwise the same functionality but the commit serves to introduce some of the data structure needed to support more general numeric expressions. 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/D60384 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@359741 91177308-0d34-0410-b5e6-96231b3b80d8 Thomas Preud'homme 6 months ago
5 changed file(s) with 393 addition(s) and 185 deletion(s). Raw diff Collapse all Expand all
101101
102102 .. option:: -D
103103
104 Sets a filecheck variable ``VAR`` with value ``VALUE`` that can be used in
105 ``CHECK:`` lines.
104 Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
105 used in ``CHECK:`` lines.
106106
107107 .. option:: -version
108108
519519 braces explicitly from the input, you can use something ugly like
520520 ``{{[{][{]}}`` as your pattern.
521521
522 FileCheck Variables
523 ~~~~~~~~~~~~~~~~~~~
522 FileCheck Pattern Expressions
523 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
524524
525525 It is often useful to match a pattern and then verify that it occurs again
526526 later in the file. For codegen tests, this can be useful to allow any register,
527527 but verify that that register is used consistently later. To do this,
528 :program:`FileCheck` allows named variables to be defined and substituted into
529 patterns. Here is a simple example:
528 :program:`FileCheck` supports pattern expressions that allow pattern variables
529 to be defined and substituted into patterns. Here is a simple example:
530530
531531 .. code-block:: llvm
532532
559559 This makes it easier to ensure that individual tests are not affected
560560 by variables set in preceding tests.
561561
562 FileCheck Expressions
563 ~~~~~~~~~~~~~~~~~~~~~
564
565 Sometimes there's a need to verify output which refers line numbers of the
562 FileCheck Numeric Expressions
563 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
564
565 Sometimes there's a need to verify output that contains line numbers of the
566566 match file, e.g. when testing compiler diagnostics. This introduces a certain
567567 fragility of the match file structure, as "``CHECK:``" lines contain absolute
568568 line numbers in the same file, which have to be updated whenever line numbers
569569 change due to text addition or deletion.
570570
571 To support this case, FileCheck allows using ``[[@LINE]]``,
572 ``[[@LINE+]]``, ``[[@LINE-]]`` expressions in patterns. These
573 expressions expand to a number of the line where a pattern is located (with an
574 optional integer offset).
571 To support this case, FileCheck allows using ``[[#@LINE]]``,
572 ``[[#@LINE+]]`` and ``[[#@LINE-]]`` numeric expressions in
573 patterns, with an arbitrary number of spaces between each element of the
574 expression. These expressions expand to the number of the line where a pattern
575 is located (with an optional integer offset).
575576
576577 This way match patterns can be put near the relevant test lines and include
577578 relative line number references, for example:
578579
579580 .. code-block:: c++
580581
581 // CHECK: test.cpp:[[@LINE+4]]:6: error: expected ';' after top level declarator
582 // CHECK: test.cpp:[[# @LINE + 4]]:6: error: expected ';' after top level declarator
582583 // CHECK-NEXT: {{^int a}}
583584 // CHECK-NEXT: {{^ \^}}
584585 // CHECK-NEXT: {{^ ;}}
585586 int a
586587
588 To support legacy uses of ``@LINE`` as a special pattern variable,
589 :program:`FileCheck` also accepts the following uses of ``@LINE`` with pattern
590 variable syntax: ``[[@LINE]]``, ``[[@LINE+]]`` and
591 ``[[@LINE-]]`` without any spaces inside the brackets and where
592 ``offset`` is an integer.
593
587594 Matching Newline Characters
588595 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
589596
3535 bool VerboseVerbose = false;
3636 };
3737
38
39 //===----------------------------------------------------------------------===//
40 // Pattern Handling Code.
38 //===----------------------------------------------------------------------===//
39 // Numeric expression handling code.
40 //===----------------------------------------------------------------------===//
41
42 /// Class representing a numeric expression.
43 class FileCheckNumExpr {
44 private:
45 /// Value of the numeric expression.
46 uint64_t Value;
47
48 public:
49 /// Constructor for a numeric expression with a known value at parse time,
50 /// e.g. the implicit numeric expression defining the @LINE numeric pseudo
51 /// variable.
52 explicit FileCheckNumExpr(uint64_t Value) : Value(Value) {}
53
54 /// Return the value being matched against.
55 uint64_t getValue() const { return Value; }
56 };
57
58 class FileCheckPatternContext;
59
60 /// Class representing a substitution to perform in the string to match.
61 class FileCheckPatternSubstitution {
62 private:
63 /// Pointer to a class instance holding the table with the values of live
64 /// pattern variables at the start of any given CHECK line. Used for
65 /// substituting pattern variables (numeric variables have their value in the
66 /// FileCheckNumExpr class instance pointed to by NumExpr).
67 FileCheckPatternContext *Context;
68
69 /// Whether this represents a numeric expression substitution.
70 bool IsNumExpr;
71
72 /// The string that needs to be substituted for something else. For a
73 /// pattern variable this is its name, otherwise this is the whole numeric
74 /// expression.
75 StringRef FromStr;
76
77 /// If this is a numeric expression substitution, this is the pointer to the
78 /// class representing that numeric expression.
79 FileCheckNumExpr *NumExpr = nullptr;
80
81 // Index in RegExStr of where to do the substitution.
82 size_t InsertIdx;
83
84 public:
85 /// Constructor for a pattern variable substitution.
86 FileCheckPatternSubstitution(FileCheckPatternContext *Context,
87 StringRef VarName, size_t InsertIdx)
88 : Context(Context), IsNumExpr(false), FromStr(VarName),
89 InsertIdx(InsertIdx) {}
90
91 /// Constructor for a numeric expression substitution.
92 FileCheckPatternSubstitution(FileCheckPatternContext *Context, StringRef Expr,
93 FileCheckNumExpr *NumExpr, size_t InsertIdx)
94 : Context(Context), IsNumExpr(true), FromStr(Expr), NumExpr(NumExpr),
95 InsertIdx(InsertIdx) {}
96
97 /// Return whether this is a numeric expression substitution.
98 bool isNumExpr() const { return IsNumExpr; }
99
100 /// Return the string to be substituted.
101 StringRef getFromString() const { return FromStr; }
102
103 /// Return the index where the substitution is to be performed.
104 size_t getIndex() const { return InsertIdx; }
105
106 /// Return the result of the substitution represented by this class instance
107 /// or None if substitution failed. For a numeric expression we substitute it
108 /// by its value. For a pattern variable we simply replace it by the text its
109 /// definition matched.
110 llvm::Optional getResult() const;
111
112 /// Return the name of the undefined variable used in this substitution, if
113 /// any, or an empty string otherwise.
114 StringRef getUndefVarName() const;
115 };
116
117 //===----------------------------------------------------------------------===//
118 // Pattern handling code.
41119 //===----------------------------------------------------------------------===//
42120
43121 namespace Check {
90168
91169 private:
92170 /// When matching a given pattern, this holds the value of all the FileCheck
93 /// variables defined in previous patterns. In a pattern only the last
94 /// definition for a given variable is recorded in this table, back-references
95 /// are used for uses after any the other definition.
171 /// pattern variables defined in previous patterns. In a pattern, only the
172 /// last definition for a given variable is recorded in this table.
173 /// Back-references are used for uses after any the other definition.
96174 StringMap GlobalVariableTable;
97175
98 public:
99 /// Return the value of variable \p VarName or None if no such variable has
100 /// been defined.
101 llvm::Optional getVarValue(StringRef VarName);
102
103 /// Define variables from definitions given on the command line passed as a
104 /// vector of VAR=VAL strings in \p CmdlineDefines. Report any error to \p SM
105 /// and return whether an error occured.
176 /// Vector holding pointers to all parsed numeric expressions. Used to
177 /// automatically free the numeric expressions once they are guaranteed to no
178 /// longer be used.
179 std::vector> NumExprs;
180
181 public:
182 /// Return the value of pattern variable \p VarName or None if no such
183 /// variable has been defined.
184 llvm::Optional getPatternVarValue(StringRef VarName);
185
186 /// Define pattern variables from definitions given on the command line,
187 /// passed as a vector of VAR=VAL strings in \p CmdlineDefines. Report any
188 /// error to \p SM and return whether an error occured.
106189 bool defineCmdlineVariables(std::vector &CmdlineDefines,
107190 SourceMgr &SM);
108191
109192 /// Undefine local variables (variables whose name does not start with a '$'
110193 /// sign), i.e. remove them from GlobalVariableTable.
111194 void clearLocalVars();
195
196 private:
197 /// Make a new numeric expression instance and register it for destruction
198 /// when the context is destroyed.
199 template FileCheckNumExpr *makeNumExpr(Types... Args);
112200 };
113201
114202 class FileCheckPattern {
122210 /// a fixed string to match.
123211 std::string RegExStr;
124212
125 /// Entries in this vector map to uses of a variable in the pattern, e.g.
126 /// "foo[[bar]]baz". In this case, the RegExStr will contain "foobaz" and
127 /// we'll get an entry in this vector that tells us to insert the value of
128 /// bar at offset 3.
129 std::vector> VariableUses;
130
131 /// Maps definitions of variables to their parenthesized capture numbers.
132 ///
133 /// E.g. for the pattern "foo[[bar:.*]]baz", VariableDefs will map "bar" to
134 /// 1.
213 /// Entries in this vector represent uses of a pattern variable or a numeric
214 /// expression in the pattern that need to be substituted in the regexp
215 /// pattern at match time, e.g. "foo[[bar]]baz[[#@LINE+1]]". In this case,
216 /// the RegExStr will contain "foobaz" and we'll get two entries in this
217 /// vector that tells us to insert the value of pattern variable "bar" at
218 /// offset 3 and the value of numeric expression "@LINE+1" at offset 6. Uses
219 /// are represented by a FileCheckPatternSubstitution class to abstract
220 /// whether it is a pattern variable or a numeric expression.
221 std::vector Substitutions;
222
223 /// Maps names of pattern variables defined in a pattern to the parenthesized
224 /// capture numbers of their last definition.
225 ///
226 /// E.g. for the pattern "foo[[bar:.*]]baz[[bar]]quux[[bar:.*]]",
227 /// VariableDefs will map "bar" to 2 corresponding to the second definition
228 /// of "bar".
229 ///
230 /// Note: uses std::map rather than StringMap to be able to get the key when
231 /// iterating over values.
135232 std::map VariableDefs;
136233
137234 /// Pointer to the class instance shared by all patterns holding a table with
164261 static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx);
165262 /// Parse a numeric expression involving pseudo variable \p Name with the
166263 /// string corresponding to the operation being performed in \p Trailer.
167 /// Return whether parsing failed in which case errors are reported on \p SM.
168 bool parseExpression(StringRef Name, StringRef Trailer,
169 const SourceMgr &SM) const;
264 /// Return the class representing the numeric expression or nullptr if
265 /// parsing fails in which case errors are reported on \p SM.
266 FileCheckNumExpr *parseNumericExpression(StringRef Name, StringRef Trailer,
267 const SourceMgr &SM) const;
170268 bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM,
171269 unsigned LineNumber, const FileCheckRequest &Req);
172270 size_t match(StringRef Buffer, size_t &MatchLen) const;
173 /// Print value of successful substitutions or name of undefined pattern
174 /// variables preventing such a successful substitution.
175 void printVariableUses(const SourceMgr &SM, StringRef Buffer,
176 SMRange MatchRange = None) const;
271 /// Print value of successful substitutions or name of undefined pattern or
272 /// numeric variables preventing such a successful substitution.
273 void printSubstitutions(const SourceMgr &SM, StringRef Buffer,
274 SMRange MatchRange = None) const;
177275 void printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
178276 std::vector *Diags) const;
179277
180278 bool hasVariable() const {
181 return !(VariableUses.empty() && VariableDefs.empty());
279 return !(Substitutions.empty() && VariableDefs.empty());
182280 }
183281
184282 Check::FileCheckType getCheckTy() const { return CheckTy; }
189287 bool AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM);
190288 void AddBackrefToRegEx(unsigned BackrefNum);
191289 unsigned computeMatchDistance(StringRef Buffer) const;
192 void evaluateExpression(StringRef Expr, std::string &Value) const;
193290 size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
194291 };
195292
2323
2424 using namespace llvm;
2525
26 llvm::Optional FileCheckPatternSubstitution::getResult() const {
27 if (IsNumExpr) {
28 return utostr(NumExpr->getValue());
29 } else {
30 // Look up the value and escape it so that we can put it into the
31 // regex.
32 llvm::Optional VarVal = Context->getPatternVarValue(FromStr);
33 if (!VarVal)
34 return llvm::None;
35 return Regex::escape(*VarVal);
36 }
37 }
38
39 StringRef FileCheckPatternSubstitution::getUndefVarName() const {
40 // Parsing guarantees only @LINE is ever referenced and it is not undefined
41 // by ClearLocalVars.
42 if (IsNumExpr)
43 return StringRef();
44
45 if (!Context->getPatternVarValue(FromStr))
46 return FromStr;
47
48 return StringRef();
49 }
50
2651 bool FileCheckPattern::isValidVarNameStart(char C) {
2752 return C == '_' || isalpha(C);
2853 }
5378 TrailIdx = I;
5479 return false;
5580 }
81
82 // StringRef holding all characters considered as horizontal whitespaces by
83 // FileCheck input canonicalization.
84 StringRef SpaceChars = " \t";
5685
5786 // Parsing helper function that strips the first character in S and returns it.
5887 static char popFront(StringRef &S) {
6190 return C;
6291 }
6392
64 bool FileCheckPattern::parseExpression(StringRef Name, StringRef Trailer,
65 const SourceMgr &SM) const {
93 FileCheckNumExpr *
94 FileCheckPattern::parseNumericExpression(StringRef Name, StringRef Trailer,
95 const SourceMgr &SM) const {
6696 if (!Name.equals("@LINE")) {
6797 SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
68 "invalid pseudo variable '" + Name + "'");
69 return true;
98 "invalid pseudo numeric variable '" + Name + "'");
99 return nullptr;
70100 }
71101
72102 // Check if this is a supported operation and select function to perform it.
103 Trailer = Trailer.ltrim(SpaceChars);
73104 if (Trailer.empty())
74 return false;
105 return Context->makeNumExpr(LineNumber);
75106 SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data());
76107 char Operator = popFront(Trailer);
108
109 // Parse right operand.
110 Trailer = Trailer.ltrim(SpaceChars);
111 if (Trailer.empty()) {
112 SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
113 "missing operand in numeric expression '" + Trailer + "'");
114 return nullptr;
115 }
116 uint64_t Offset;
117 if (Trailer.consumeInteger(10, Offset)) {
118 SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
119 "invalid offset in numeric expression '" + Trailer + "'");
120 return nullptr;
121 }
122 Trailer = Trailer.ltrim(SpaceChars);
123 if (!Trailer.empty()) {
124 SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
125 "unexpected characters at end of numeric expression '" +
126 Trailer + "'");
127 return nullptr;
128 }
129
130 uint64_t Value;
77131 switch (Operator) {
78132 case '+':
133 Value = LineNumber + Offset;
134 break;
79135 case '-':
136 Value = LineNumber - Offset;
80137 break;
81138 default:
82139 SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
83140 Twine("unsupported numeric operation '") + Twine(Operator) +
84141 "'");
85 return true;
86 }
87
88 // Parse right operand.
89 if (Trailer.empty()) {
90 SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
91 "missing operand in numeric expression '" + Trailer + "'");
92 return true;
93 }
94 uint64_t Offset;
95 if (Trailer.consumeInteger(10, Offset)) {
96 SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
97 "invalid offset in numeric expression '" + Trailer + "'");
98 return true;
99 }
100 if (!Trailer.empty()) {
101 SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
102 "unexpected characters at end of numeric expression '" +
103 Trailer + "'");
104 return true;
105 }
106 return false;
142 return nullptr;
143 }
144 return Context->makeNumExpr(Value);
107145 }
108146
109147 /// Parses the given string into the Pattern.
193231 continue;
194232 }
195233
196 // Named RegEx matches. These are of two forms: [[foo:.*]] which matches .*
197 // (or some other regex) and assigns it to the FileCheck variable 'foo'. The
198 // second form is [[foo]] which is a reference to foo. The variable name
199 // itself must be of the form "[a-zA-Z_][0-9a-zA-Z_]*", otherwise we reject
200 // it. This is to catch some common errors.
234 // Pattern and numeric expression matches. Pattern expressions come in two
235 // forms: [[foo:.*]] and [[foo]]. The former matches .* (or some other
236 // regex) and assigns it to the FileCheck variable 'foo'. The latter
237 // substitutes foo's value. Numeric expressions start with a '#' sign after
238 // the double brackets and only have the substitution form. Pattern
239 // variables must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*"
240 // to be valid, as this helps catch some common errors. Numeric expressions
241 // only support the @LINE pseudo numeric variable.
201242 if (PatternStr.startswith("[[")) {
202 StringRef MatchStr = PatternStr.substr(2);
243 StringRef UnparsedPatternStr = PatternStr.substr(2);
203244 // Find the closing bracket pair ending the match. End is going to be an
204245 // offset relative to the beginning of the match string.
205 size_t End = FindRegexVarEnd(MatchStr, SM);
246 size_t End = FindRegexVarEnd(UnparsedPatternStr, SM);
247 StringRef MatchStr = UnparsedPatternStr.substr(0, End);
248 bool IsNumExpr = MatchStr.consume_front("#");
249 const char *RefTypeStr =
250 IsNumExpr ? "numeric expression" : "pattern variable";
206251
207252 if (End == StringRef::npos) {
208 SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
209 SourceMgr::DK_Error,
210 "invalid named regex reference, no ]] found");
253 SM.PrintMessage(
254 SMLoc::getFromPointer(PatternStr.data()), SourceMgr::DK_Error,
255 Twine("Invalid ") + RefTypeStr + " reference, no ]] found");
211256 return true;
212257 }
213
214 MatchStr = MatchStr.substr(0, End);
215 PatternStr = PatternStr.substr(End + 4);
258 // Strip the subtitution we are parsing. End points to the start of the
259 // "]]" closing the expression so account for it in computing the index
260 // of the first unparsed character.
261 PatternStr = UnparsedPatternStr.substr(End + 2);
216262
217263 size_t VarEndIdx = MatchStr.find(":");
218 size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
219 if (SpacePos != StringRef::npos) {
220 SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
221 SourceMgr::DK_Error, "unexpected whitespace");
222 return true;
264 if (IsNumExpr)
265 MatchStr = MatchStr.ltrim(SpaceChars);
266 else {
267 size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
268 if (SpacePos != StringRef::npos) {
269 SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
270 SourceMgr::DK_Error, "unexpected whitespace");
271 return true;
272 }
223273 }
224274
225275 // Get the regex name (e.g. "foo") and verify it is well formed.
227277 unsigned TrailIdx;
228278 if (parseVariable(MatchStr, IsPseudo, TrailIdx)) {
229279 SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
230 SourceMgr::DK_Error, "invalid name in named regex");
280 SourceMgr::DK_Error, "invalid variable name");
231281 return true;
232282 }
283
284 size_t SubstInsertIdx = RegExStr.size();
285 FileCheckNumExpr *NumExpr;
233286
234287 StringRef Name = MatchStr.substr(0, TrailIdx);
235288 StringRef Trailer = MatchStr.substr(TrailIdx);
238291 if (IsVarDef && (IsPseudo || !Trailer.consume_front(":"))) {
239292 SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
240293 SourceMgr::DK_Error,
241 "invalid name in named regex definition");
294 "invalid name in pattern variable definition");
242295 return true;
243296 }
244297
245298 if (!IsVarDef && IsPseudo) {
246 if (parseExpression(Name, Trailer, SM))
299 NumExpr = parseNumericExpression(Name, Trailer, SM);
300 if (NumExpr == nullptr)
247301 return true;
302 IsNumExpr = true;
248303 }
249304
250305 // Handle [[foo]].
251306 if (!IsVarDef) {
252 // Handle variables that were defined earlier on the same line by
253 // emitting a backreference.
254 if (VariableDefs.find(Name) != VariableDefs.end()) {
255 unsigned VarParenNum = VariableDefs[Name];
256 if (VarParenNum < 1 || VarParenNum > 9) {
307 // Handle use of pattern variables that were defined earlier on the
308 // same line by emitting a backreference.
309 if (!IsNumExpr && VariableDefs.find(Name) != VariableDefs.end()) {
310 unsigned CaptureParen = VariableDefs[Name];
311 if (CaptureParen < 1 || CaptureParen > 9) {
257312 SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
258313 SourceMgr::DK_Error,
259314 "Can't back-reference more than 9 variables");
260315 return true;
261316 }
262 AddBackrefToRegEx(VarParenNum);
317 AddBackrefToRegEx(CaptureParen);
263318 } else {
264 VariableUses.push_back(std::make_pair(MatchStr, RegExStr.size()));
319 // Handle use of pattern variables ([[]]) defined in previous
320 // CHECK pattern or use of a numeric expression.
321 FileCheckPatternSubstitution Substitution =
322 IsNumExpr ? FileCheckPatternSubstitution(Context, MatchStr,
323 NumExpr, SubstInsertIdx)
324 : FileCheckPatternSubstitution(Context, MatchStr,
325 SubstInsertIdx);
326 Substitutions.push_back(Substitution);
265327 }
266328 continue;
267329 }
314376 RegExStr += Backref;
315377 }
316378
317 /// Evaluates expression and stores the result to \p Value.
318 void FileCheckPattern::evaluateExpression(StringRef Expr,
319 std::string &Value) const {
320 Expr = Expr.substr(StringRef("@LINE").size());
321 int Offset = 0;
322 if (!Expr.empty()) {
323 if (Expr[0] == '+')
324 Expr = Expr.substr(1);
325 Expr.getAsInteger(10, Offset);
326 }
327 Value = llvm::itostr(LineNumber + Offset);
328 }
329
330379 /// Matches the pattern string against the input buffer \p Buffer
331380 ///
332381 /// This returns the position that is matched or npos if there is no match. If
334383 /// MatchLen.
335384 ///
336385 /// The GlobalVariableTable StringMap in the FileCheckPatternContext class
337 /// instance provides the current values of FileCheck variables and is updated
338 /// if this match defines new values.
386 /// instance provides the current values of FileCheck pattern variables and is
387 /// updated if this match defines new values.
339388 size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
340389 // If this is the EOF pattern, match it immediately.
341390 if (CheckTy == Check::CheckEOF) {
355404 // actual value.
356405 StringRef RegExToMatch = RegExStr;
357406 std::string TmpStr;
358 if (!VariableUses.empty()) {
407 if (!Substitutions.empty()) {
359408 TmpStr = RegExStr;
360409
361 unsigned InsertOffset = 0;
362 for (const auto &VariableUse : VariableUses) {
363 std::string Value;
364
365 if (VariableUse.first[0] == '@') {
366 evaluateExpression(VariableUse.first, Value);
367 } else {
368 llvm::Optional ValueRef =
369 Context->getVarValue(VariableUse.first);
370 // If the variable is undefined, return an error.
371 if (!ValueRef)
372 return StringRef::npos;
373
374 // Look up the value and escape it so that we can put it into the regex.
375 Value += Regex::escape(*ValueRef);
376 }
410 size_t InsertOffset = 0;
411 // Substitute all pattern variables and numeric expressions whose value is
412 // known just now. Use of pattern variables defined on the same line are
413 // handled by back-references.
414 for (const auto &Substitution : Substitutions) {
415 // Substitute and check for failure (e.g. use of undefined variable).
416 llvm::Optional Value = Substitution.getResult();
417 if (!Value)
418 return StringRef::npos;
377419
378420 // Plop it into the regex at the adjusted offset.
379 TmpStr.insert(TmpStr.begin() + VariableUse.second + InsertOffset,
380 Value.begin(), Value.end());
381 InsertOffset += Value.size();
421 TmpStr.insert(TmpStr.begin() + Substitution.getIndex() + InsertOffset,
422 Value->begin(), Value->end());
423 InsertOffset += Value->size();
382424 }
383425
384426 // Match the newly constructed regex.
393435 assert(!MatchInfo.empty() && "Didn't get any match");
394436 StringRef FullMatch = MatchInfo[0];
395437
396 // If this defines any variables, remember their values.
438 // If this defines any pattern variables, remember their values.
397439 for (const auto &VariableDef : VariableDefs) {
398440 assert(VariableDef.second < MatchInfo.size() && "Internal paren error");
399441 Context->GlobalVariableTable[VariableDef.first] =
428470 return BufferPrefix.edit_distance(ExampleString);
429471 }
430472
431 void FileCheckPattern::printVariableUses(const SourceMgr &SM, StringRef Buffer,
432 SMRange MatchRange) const {
433 // If this was a regular expression using variables, print the current
434 // variable values.
435 if (!VariableUses.empty()) {
436 for (const auto &VariableUse : VariableUses) {
473 void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
474 SMRange MatchRange) const {
475 // Print what we know about substitutions. This covers both uses of pattern
476 // variables and numeric subsitutions.
477 if (!Substitutions.empty()) {
478 for (const auto &Substitution : Substitutions) {
437479 SmallString<256> Msg;
438480 raw_svector_ostream OS(Msg);
439 StringRef Var = VariableUse.first;
440 if (Var[0] == '@') {
441 std::string Value;
442 evaluateExpression(Var, Value);
443 OS << "with expression \"";
444 OS.write_escaped(Var) << "\" equal to \"";
445 OS.write_escaped(Value) << "\"";
481 bool IsNumExpr = Substitution.isNumExpr();
482 llvm::Optional MatchedValue = Substitution.getResult();
483
484 // Substitution failed or is not known at match time, print the undefined
485 // variable it uses.
486 if (!MatchedValue) {
487 StringRef UndefVarName = Substitution.getUndefVarName();
488 if (UndefVarName.empty())
489 continue;
490 OS << "uses undefined variable \"";
491 OS.write_escaped(UndefVarName) << "\"";
446492 } else {
447 llvm::Optional VarValue = Context->getVarValue(Var);
448
449 // Check for undefined variable references.
450 if (!VarValue) {
451 OS << "uses undefined variable \"";
452 OS.write_escaped(Var) << "\"";
453 } else {
493 // Substitution succeeded. Print substituted value.
494 if (IsNumExpr)
495 OS << "with numeric expression \"";
496 else
454497 OS << "with variable \"";
455 OS.write_escaped(Var) << "\" equal to \"";
456 OS.write_escaped(*VarValue) << "\"";
457 }
498 OS.write_escaped(Substitution.getFromString()) << "\" equal to \"";
499 OS.write_escaped(*MatchedValue) << "\"";
458500 }
459501
460502 if (MatchRange.isValid())
533575 }
534576
535577 llvm::Optional
536 FileCheckPatternContext::getVarValue(StringRef VarName) {
578 FileCheckPatternContext::getPatternVarValue(StringRef VarName) {
537579 auto VarIter = GlobalVariableTable.find(VarName);
538580 if (VarIter == GlobalVariableTable.end())
539581 return llvm::None;
540582
541583 return VarIter->second;
584 }
585
586 template
587 FileCheckNumExpr *FileCheckPatternContext::makeNumExpr(Types... Args) {
588 NumExprs.emplace_back(new FileCheckNumExpr(Args...));
589 return NumExprs.back().get();
542590 }
543591
544592 /// Finds the closing sequence of a regex variable usage or definition.
9971045 Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
9981046 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
9991047 {MatchRange});
1000 Pat.printVariableUses(SM, Buffer, MatchRange);
1048 Pat.printSubstitutions(SM, Buffer, MatchRange);
10011049 }
10021050
10031051 static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
10481096 SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here");
10491097
10501098 // Allow the pattern to print additional information if desired.
1051 Pat.printVariableUses(SM, Buffer);
1099 Pat.printSubstitutions(SM, Buffer);
10521100
10531101 if (ExpectedMatch)
10541102 Pat.printFuzzyMatch(SM, Buffer, Diags);
None ; RUN: FileCheck -input-file %s %s
0 ; RUN: FileCheck -input-file %s %s
11 ; RUN: not FileCheck -check-prefix BAD1 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR1 %s
22 ; RUN: not FileCheck -check-prefix BAD2 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR2 %s
33 ; RUN: not FileCheck -check-prefix BAD3 -input-file %s %s 2>&1 | FileCheck -check-prefix ERR3 %s
2222 23 arst CHECK: [[@LINE]] {{a}}rst
2323 24
2424 25 BAD1: [[@LINE:cant-have-regex]]
25 26 ERR1: line-count.txt:[[@LINE-1]]:12: error: invalid name in named regex definition
25 26 ERR1: line-count.txt:[[#@LINE-1]]:12: error: invalid name in pattern variable definition
2626 27
2727 28 BAD2: [[ @LINE]]
28 29 ERR2: line-count.txt:[[@LINE-1]]:12: error: unexpected whitespace
28 29 ERR2: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace
2929 30
3030 31 BAD3: [[@LINE ]]
31 32 ERR3: line-count.txt:[[@LINE-1]]:17: error: unexpected whitespace
31 32 ERR3: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace
3232 33
3333 34 BAD4: [[ @LINE-1]]
34 35 ERR4: line-count.txt:[[@LINE-1]]:12: error: unexpected whitespace
34 35 ERR4: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace
3535 36
3636 37 BAD5: [[@LINE -1]]
37 38 ERR5: line-count.txt:[[@LINE-1]]:17: error: unexpected whitespace
37 38 ERR5: line-count.txt:[[#@LINE-1]]:17: error: unexpected whitespace
3838 39
3939 40 BAD6: [[@LINE- 1]]
40 41 ERR6: line-count.txt:[[@LINE-1]]:18: error: unexpected whitespace
40 41 ERR6: line-count.txt:[[#@LINE-1]]:18: error: unexpected whitespace
4141 42
4242 43 BAD7: [[@LINE-1 ]]
43 44 ERR7: line-count.txt:[[@LINE-1]]:19: error: unexpected whitespace
43 44 ERR7: line-count.txt:[[#@LINE-1]]:19: error: unexpected whitespace
4444 45
4545 46 BAD8: [[@LIN]]
46 47 ERR8: line-count.txt:[[@LINE-1]]:12: error: invalid pseudo variable '@LIN'
46 47 ERR8: line-count.txt:[[#@LINE-1]]:12: error: invalid pseudo numeric variable '@LIN'
4747 48
4848 49 BAD9: [[@LINE*2]]
49 50 ERR9: line-count.txt:[[@LINE-1]]:17: error: unsupported numeric operation '*'
49 50 ERR9: line-count.txt:[[#@LINE-1]]:17: error: unsupported numeric operation '*'
5050 51
5151 52 BAD10: [[@LINE-x]]
52 53 ERR10: line-count.txt:[[@LINE-1]]:19: error: invalid offset in numeric expression 'x'
52 53 ERR10: line-count.txt:[[#@LINE-1]]:19: error: invalid offset in numeric expression 'x'
5353 54
5454 55 BAD11: [[@LINE-1x]]
55 56 ERR11: line-count.txt:[[@LINE-1]]:19: error: unexpected characters at end of numeric expression 'x'
55 56 ERR11: line-count.txt:[[#@LINE-1]]:19: error: unexpected characters at end of numeric expression 'x'
56 57
57 58 CHECK: [[#@LINE]] CHECK
58 59 CHECK: [[# @LINE]] CHECK
59 60 CHECK: [[# @LINE ]] CHECK
60 61
61 62 CHECK: [[#@LINE-1]]
62 63 CHECK: [[# @LINE-1]] CHECK
63 64 CHECK: [[# @LINE -1]] CHECK
64 65 CHECK: [[# @LINE - 1]] CHECK
65 66 CHECK: [[# @LINE - 1 ]] CHECK
104104 SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
105105 StringRef VarNameRef = NameTrailerRef.substr(0, VarName.size());
106106 StringRef TrailerRef = NameTrailerRef.substr(VarName.size());
107 return P.parseExpression(VarNameRef, TrailerRef, SM);
107 return P.parseNumericExpression(VarNameRef, TrailerRef, SM) == nullptr;
108108 }
109109 };
110110
143143 VarName = "@LINE";
144144 Trailer = "+5x";
145145 EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
146 }
147
148 TEST_F(FileCheckTest, Substitution) {
149 SourceMgr SM;
150 FileCheckPatternContext Context;
151 std::vector GlobalDefines;
152 GlobalDefines.emplace_back(std::string("FOO=BAR"));
153 Context.defineCmdlineVariables(GlobalDefines, SM);
154
155 FileCheckPatternSubstitution Substitution =
156 FileCheckPatternSubstitution(&Context, "VAR404", 42);
157 EXPECT_FALSE(Substitution.getResult());
158
159 FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
160 Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
161 llvm::Optional Value = Substitution.getResult();
162 EXPECT_TRUE(Value);
163 EXPECT_EQ("42", *Value);
164
165 FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
166 Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
167 Value = Substitution.getResult();
168 EXPECT_TRUE(Value);
169 EXPECT_EQ("BAR", *Value);
170 }
171
172 TEST_F(FileCheckTest, UndefVars) {
173 SourceMgr SM;
174 FileCheckPatternContext Context;
175 std::vector GlobalDefines;
176 GlobalDefines.emplace_back(std::string("FOO=BAR"));
177 Context.defineCmdlineVariables(GlobalDefines, SM);
178
179 FileCheckPatternSubstitution Substitution =
180 FileCheckPatternSubstitution(&Context, "VAR404", 42);
181 StringRef UndefVar = Substitution.getUndefVarName();
182 EXPECT_EQ("VAR404", UndefVar);
183
184 FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
185 Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
186 UndefVar = Substitution.getUndefVarName();
187 EXPECT_EQ("", UndefVar);
188
189 Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
190 UndefVar = Substitution.getUndefVarName();
191 EXPECT_EQ("", UndefVar);
146192 }
147193
148194 TEST_F(FileCheckTest, FileCheckContext) {
175221 StringRef LocalVarStr = "LocalVar";
176222 StringRef EmptyVarStr = "EmptyVar";
177223 StringRef UnknownVarStr = "UnknownVar";
178 llvm::Optional LocalVar = Cxt.getVarValue(LocalVarStr);
179 llvm::Optional EmptyVar = Cxt.getVarValue(EmptyVarStr);
180 llvm::Optional UnknownVar = Cxt.getVarValue(UnknownVarStr);
224 llvm::Optional LocalVar = Cxt.getPatternVarValue(LocalVarStr);
225 llvm::Optional EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
226 llvm::Optional UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
181227 EXPECT_TRUE(LocalVar);
182228 EXPECT_EQ(*LocalVar, "FOO");
183229 EXPECT_TRUE(EmptyVar);
186232
187233 // Clear local variables and check they become absent.
188234 Cxt.clearLocalVars();
189 LocalVar = Cxt.getVarValue(LocalVarStr);
235 LocalVar = Cxt.getPatternVarValue(LocalVarStr);
190236 EXPECT_FALSE(LocalVar);
191 EmptyVar = Cxt.getVarValue(EmptyVarStr);
237 EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
192238 EXPECT_FALSE(EmptyVar);
193239
194240 // Redefine global variables and check variables are defined again.
196242 GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
197243 EXPECT_FALSE(GotError);
198244 StringRef GlobalVarStr = "$GlobalVar";
199 llvm::Optional GlobalVar = Cxt.getVarValue(GlobalVarStr);
245 llvm::Optional GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
200246 EXPECT_TRUE(GlobalVar);
201247 EXPECT_EQ(*GlobalVar, "BAR");
202248
203249 // Clear local variables and check global variables remain defined.
204250 Cxt.clearLocalVars();
205 GlobalVar = Cxt.getVarValue(GlobalVarStr);
251 GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
206252 EXPECT_TRUE(GlobalVar);
207253 }
208254 } // namespace