blob: 03e892af274668b85503a64fec7f174901e7ed4d [file] [log] [blame]
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001//===- FileCheck.cpp - Check that File's Contents match what is expected --===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00006//
7//===----------------------------------------------------------------------===//
8//
9// FileCheck does a line-by line check of a file that validates whether it
10// contains the expected content. This is useful for regression tests etc.
11//
12// This file implements most of the API that will be used by the FileCheck utility
13// as well as various unittests.
14//===----------------------------------------------------------------------===//
15
16#include "llvm/Support/FileCheck.h"
17#include "llvm/ADT/StringSet.h"
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +000018#include "llvm/Support/FormatVariadic.h"
19#include <cstdint>
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +000020#include <list>
21#include <map>
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +000022#include <tuple>
23#include <utility>
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +000024
25using namespace llvm;
26
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000027bool FileCheckNumericVariable::setValue(uint64_t NewValue) {
28 if (Value)
29 return true;
30 Value = NewValue;
31 return false;
32}
33
34bool FileCheckNumericVariable::clearValue() {
35 if (!Value)
36 return true;
Thomas Preud'homme7b7683d2019-05-23 17:19:36 +000037 Value = None;
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000038 return false;
39}
40
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +000041Expected<uint64_t> FileCheckExpression::eval() const {
42 assert(LeftOp && "Evaluating an empty expression");
Thomas Preud'homme71d3f222019-06-06 13:21:06 +000043 Optional<uint64_t> LeftOpValue = LeftOp->getValue();
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000044 // Variable is undefined.
Thomas Preud'homme71d3f222019-06-06 13:21:06 +000045 if (!LeftOpValue)
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000046 return make_error<FileCheckUndefVarError>(LeftOp->getName());
Thomas Preud'homme71d3f222019-06-06 13:21:06 +000047 return EvalBinop(*LeftOpValue, RightOp);
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000048}
49
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000050Expected<std::string> FileCheckNumericSubstitution::getResult() const {
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +000051 Expected<uint64_t> EvaluatedValue = Expression->eval();
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +000052 if (!EvaluatedValue)
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000053 return EvaluatedValue.takeError();
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +000054 return utostr(*EvaluatedValue);
55}
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000056
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000057Expected<std::string> FileCheckStringSubstitution::getResult() const {
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000058 // Look up the value and escape it so that we can put it into the regex.
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000059 Expected<StringRef> VarVal = Context->getPatternVarValue(FromStr);
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000060 if (!VarVal)
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000061 return VarVal.takeError();
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000062 return Regex::escape(*VarVal);
Thomas Preud'homme288ed912019-05-02 00:04:38 +000063}
64
Thomas Preud'homme5a330472019-04-29 13:32:36 +000065bool FileCheckPattern::isValidVarNameStart(char C) {
66 return C == '_' || isalpha(C);
67}
68
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000069Expected<StringRef> FileCheckPattern::parseVariable(StringRef &Str,
70 bool &IsPseudo,
71 const SourceMgr &SM) {
Thomas Preud'homme5a330472019-04-29 13:32:36 +000072 if (Str.empty())
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000073 return FileCheckErrorDiagnostic::get(SM, Str, "empty variable name");
Thomas Preud'homme5a330472019-04-29 13:32:36 +000074
75 bool ParsedOneChar = false;
76 unsigned I = 0;
77 IsPseudo = Str[0] == '@';
78
79 // Global vars start with '$'.
80 if (Str[0] == '$' || IsPseudo)
81 ++I;
82
83 for (unsigned E = Str.size(); I != E; ++I) {
84 if (!ParsedOneChar && !isValidVarNameStart(Str[I]))
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000085 return FileCheckErrorDiagnostic::get(SM, Str, "invalid variable name");
Thomas Preud'homme5a330472019-04-29 13:32:36 +000086
87 // Variable names are composed of alphanumeric characters and underscores.
88 if (Str[I] != '_' && !isalnum(Str[I]))
89 break;
90 ParsedOneChar = true;
91 }
92
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000093 StringRef Name = Str.take_front(I);
Thomas Preud'homme71d3f222019-06-06 13:21:06 +000094 Str = Str.substr(I);
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +000095 return Name;
Thomas Preud'homme5a330472019-04-29 13:32:36 +000096}
97
Thomas Preud'homme288ed912019-05-02 00:04:38 +000098// StringRef holding all characters considered as horizontal whitespaces by
99// FileCheck input canonicalization.
100StringRef SpaceChars = " \t";
101
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000102// Parsing helper function that strips the first character in S and returns it.
103static char popFront(StringRef &S) {
104 char C = S.front();
105 S = S.drop_front();
106 return C;
107}
108
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000109char FileCheckUndefVarError::ID = 0;
110char FileCheckErrorDiagnostic::ID = 0;
111char FileCheckNotFoundError::ID = 0;
112
Thomas Preud'homme56f63082019-07-05 16:25:46 +0000113Expected<FileCheckNumericVariable *>
114FileCheckPattern::parseNumericVariableDefinition(
115 StringRef &Expr, FileCheckPatternContext *Context, size_t LineNumber,
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000116 const SourceMgr &SM) {
117 bool IsPseudo;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000118 Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM);
119 if (!ParseVarResult)
120 return ParseVarResult.takeError();
Thomas Preud'homme56f63082019-07-05 16:25:46 +0000121 StringRef Name = *ParseVarResult;
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000122
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000123 if (IsPseudo)
124 return FileCheckErrorDiagnostic::get(
125 SM, Name, "definition of pseudo numeric variable unsupported");
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000126
127 // Detect collisions between string and numeric variables when the latter
128 // is created later than the former.
129 if (Context->DefinedVariableTable.find(Name) !=
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000130 Context->DefinedVariableTable.end())
131 return FileCheckErrorDiagnostic::get(
132 SM, Name, "string variable with name '" + Name + "' already exists");
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000133
Thomas Preud'homme28196a52019-07-05 12:01:06 +0000134 Expr = Expr.ltrim(SpaceChars);
135 if (!Expr.empty())
136 return FileCheckErrorDiagnostic::get(
137 SM, Expr, "unexpected characters after numeric variable name");
138
Thomas Preud'homme56f63082019-07-05 16:25:46 +0000139 FileCheckNumericVariable *DefinedNumericVariable;
140 auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
141 if (VarTableIter != Context->GlobalNumericVariableTable.end())
142 DefinedNumericVariable = VarTableIter->second;
143 else
144 DefinedNumericVariable = Context->makeNumericVariable(LineNumber, Name);
145
146 return DefinedNumericVariable;
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000147}
148
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000149Expected<FileCheckNumericVariable *>
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000150FileCheckPattern::parseNumericVariableUse(StringRef &Expr,
151 const SourceMgr &SM) const {
152 bool IsPseudo;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000153 Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM);
154 if (!ParseVarResult)
155 return ParseVarResult.takeError();
156 StringRef Name = *ParseVarResult;
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000157
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000158 if (IsPseudo && !Name.equals("@LINE"))
159 return FileCheckErrorDiagnostic::get(
160 SM, Name, "invalid pseudo numeric variable '" + Name + "'");
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000161
Thomas Preud'homme41f2bea2019-07-05 12:01:12 +0000162 // Numeric variable definitions and uses are parsed in the order in which
163 // they appear in the CHECK patterns. For each definition, the pointer to the
164 // class instance of the corresponding numeric variable definition is stored
Thomas Preud'homme56f63082019-07-05 16:25:46 +0000165 // in GlobalNumericVariableTable in parsePattern. Therefore, if the pointer
166 // we get below is null, it means no such variable was defined before. When
167 // that happens, we create a dummy variable so that parsing can continue. All
168 // uses of undefined variables, whether string or numeric, are then diagnosed
169 // in printSubstitutions() after failing to match.
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000170 auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
Thomas Preud'hommefe7ac172019-07-05 16:25:33 +0000171 FileCheckNumericVariable *NumericVariable;
172 if (VarTableIter != Context->GlobalNumericVariableTable.end())
173 NumericVariable = VarTableIter->second;
174 else {
175 NumericVariable = Context->makeNumericVariable(0, Name);
176 Context->GlobalNumericVariableTable[Name] = NumericVariable;
177 }
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000178
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000179 if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber)
180 return FileCheckErrorDiagnostic::get(
181 SM, Name,
182 "numeric variable '" + Name + "' defined on the same line as used");
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000183
184 return NumericVariable;
185}
186
187static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
188 return LeftOp + RightOp;
189}
190
191static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
192 return LeftOp - RightOp;
193}
194
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000195Expected<FileCheckExpression *>
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000196FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const {
197 Expected<FileCheckNumericVariable *> LeftParseResult =
198 parseNumericVariableUse(Expr, SM);
199 if (!LeftParseResult) {
200 return LeftParseResult.takeError();
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000201 }
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000202 FileCheckNumericVariable *LeftOp = *LeftParseResult;
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000203
204 // Check if this is a supported operation and select a function to perform
205 // it.
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000206 Expr = Expr.ltrim(SpaceChars);
207 if (Expr.empty())
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000208 return Context->makeExpression(add, LeftOp, 0);
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000209 SMLoc OpLoc = SMLoc::getFromPointer(Expr.data());
210 char Operator = popFront(Expr);
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000211 binop_eval_t EvalBinop;
212 switch (Operator) {
213 case '+':
214 EvalBinop = add;
215 break;
216 case '-':
217 EvalBinop = sub;
218 break;
219 default:
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000220 return FileCheckErrorDiagnostic::get(
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000221 SM, OpLoc, Twine("unsupported operation '") + Twine(Operator) + "'");
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000222 }
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000223
224 // Parse right operand.
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000225 Expr = Expr.ltrim(SpaceChars);
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000226 if (Expr.empty())
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000227 return FileCheckErrorDiagnostic::get(SM, Expr,
228 "missing operand in expression");
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000229 uint64_t RightOp;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000230 if (Expr.consumeInteger(10, RightOp))
231 return FileCheckErrorDiagnostic::get(
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000232 SM, Expr, "invalid offset in expression '" + Expr + "'");
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000233 Expr = Expr.ltrim(SpaceChars);
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000234 if (!Expr.empty())
235 return FileCheckErrorDiagnostic::get(
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000236 SM, Expr, "unexpected characters at end of expression '" + Expr + "'");
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000237
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000238 return Context->makeExpression(EvalBinop, LeftOp, RightOp);
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000239}
240
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000241Expected<FileCheckExpression *> FileCheckPattern::parseNumericSubstitutionBlock(
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000242 StringRef Expr,
243 Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000244 const SourceMgr &SM) const {
245 // Parse the numeric variable definition.
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000246 DefinedNumericVariable = None;
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000247 size_t DefEnd = Expr.find(':');
248 if (DefEnd != StringRef::npos) {
249 StringRef DefExpr = Expr.substr(0, DefEnd);
Thomas Preud'homme28196a52019-07-05 12:01:06 +0000250 StringRef UseExpr = Expr.substr(DefEnd + 1);
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000251
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000252 UseExpr = UseExpr.ltrim(SpaceChars);
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000253 if (!UseExpr.empty())
254 return FileCheckErrorDiagnostic::get(
255 SM, UseExpr,
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000256 "unexpected string after variable definition: '" + UseExpr + "'");
Thomas Preud'homme28196a52019-07-05 12:01:06 +0000257
258 DefExpr = DefExpr.ltrim(SpaceChars);
Thomas Preud'homme56f63082019-07-05 16:25:46 +0000259 Expected<FileCheckNumericVariable *> ParseResult =
260 parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM);
261 if (!ParseResult)
262 return ParseResult.takeError();
263 DefinedNumericVariable = *ParseResult;
Thomas Preud'homme28196a52019-07-05 12:01:06 +0000264
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000265 return Context->makeExpression(add, nullptr, 0);
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000266 }
267
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000268 // Parse the expression itself.
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000269 Expr = Expr.ltrim(SpaceChars);
270 return parseBinop(Expr, SM);
271}
272
273bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
274 SourceMgr &SM,
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000275 const FileCheckRequest &Req) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000276 bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
277
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000278 PatternLoc = SMLoc::getFromPointer(PatternStr.data());
279
280 if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
281 // Ignore trailing whitespace.
282 while (!PatternStr.empty() &&
283 (PatternStr.back() == ' ' || PatternStr.back() == '\t'))
284 PatternStr = PatternStr.substr(0, PatternStr.size() - 1);
285
286 // Check that there is something on the line.
287 if (PatternStr.empty() && CheckTy != Check::CheckEmpty) {
288 SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
289 "found empty check string with prefix '" + Prefix + ":'");
290 return true;
291 }
292
293 if (!PatternStr.empty() && CheckTy == Check::CheckEmpty) {
294 SM.PrintMessage(
295 PatternLoc, SourceMgr::DK_Error,
296 "found non-empty check string for empty check with prefix '" + Prefix +
297 ":'");
298 return true;
299 }
300
301 if (CheckTy == Check::CheckEmpty) {
302 RegExStr = "(\n$)";
303 return false;
304 }
305
306 // Check to see if this is a fixed string, or if it has regex pieces.
307 if (!MatchFullLinesHere &&
308 (PatternStr.size() < 2 || (PatternStr.find("{{") == StringRef::npos &&
309 PatternStr.find("[[") == StringRef::npos))) {
310 FixedStr = PatternStr;
311 return false;
312 }
313
314 if (MatchFullLinesHere) {
315 RegExStr += '^';
316 if (!Req.NoCanonicalizeWhiteSpace)
317 RegExStr += " *";
318 }
319
320 // Paren value #0 is for the fully matched string. Any new parenthesized
321 // values add from there.
322 unsigned CurParen = 1;
323
324 // Otherwise, there is at least one regex piece. Build up the regex pattern
325 // by escaping scary characters in fixed strings, building up one big regex.
326 while (!PatternStr.empty()) {
327 // RegEx matches.
328 if (PatternStr.startswith("{{")) {
329 // This is the start of a regex match. Scan for the }}.
330 size_t End = PatternStr.find("}}");
331 if (End == StringRef::npos) {
332 SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
333 SourceMgr::DK_Error,
334 "found start of regex string with no end '}}'");
335 return true;
336 }
337
338 // Enclose {{}} patterns in parens just like [[]] even though we're not
339 // capturing the result for any purpose. This is required in case the
340 // expression contains an alternation like: CHECK: abc{{x|z}}def. We
341 // want this to turn into: "abc(x|z)def" not "abcx|zdef".
342 RegExStr += '(';
343 ++CurParen;
344
345 if (AddRegExToRegEx(PatternStr.substr(2, End - 2), CurParen, SM))
346 return true;
347 RegExStr += ')';
348
349 PatternStr = PatternStr.substr(End + 2);
350 continue;
351 }
352
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000353 // String and numeric substitution blocks. String substitution blocks come
354 // in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some
355 // other regex) and assigns it to the string variable 'foo'. The latter
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000356 // substitutes foo's value. Numeric substitution blocks work the same way
357 // as string ones, but start with a '#' sign after the double brackets.
358 // Both string and numeric variable names must satisfy the regular
359 // expression "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch
360 // some common errors.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000361 if (PatternStr.startswith("[[")) {
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000362 StringRef UnparsedPatternStr = PatternStr.substr(2);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000363 // Find the closing bracket pair ending the match. End is going to be an
364 // offset relative to the beginning of the match string.
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000365 size_t End = FindRegexVarEnd(UnparsedPatternStr, SM);
366 StringRef MatchStr = UnparsedPatternStr.substr(0, End);
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000367 bool IsNumBlock = MatchStr.consume_front("#");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000368
369 if (End == StringRef::npos) {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000370 SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
371 SourceMgr::DK_Error,
372 "Invalid substitution block, no ]] found");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000373 return true;
374 }
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000375 // Strip the substitution block we are parsing. End points to the start
376 // of the "]]" closing the expression so account for it in computing the
377 // index of the first unparsed character.
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000378 PatternStr = UnparsedPatternStr.substr(End + 2);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000379
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000380 bool IsDefinition = false;
381 StringRef DefName;
382 StringRef SubstStr;
383 StringRef MatchRegexp;
384 size_t SubstInsertIdx = RegExStr.size();
385
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000386 // Parse string variable or legacy expression.
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000387 if (!IsNumBlock) {
388 size_t VarEndIdx = MatchStr.find(":");
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000389 size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
390 if (SpacePos != StringRef::npos) {
391 SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
392 SourceMgr::DK_Error, "unexpected whitespace");
393 return true;
394 }
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000395
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000396 // Get the name (e.g. "foo") and verify it is well formed.
397 bool IsPseudo;
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000398 StringRef OrigMatchStr = MatchStr;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000399 Expected<StringRef> ParseVarResult =
400 parseVariable(MatchStr, IsPseudo, SM);
401 if (!ParseVarResult) {
402 logAllUnhandledErrors(ParseVarResult.takeError(), errs());
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000403 return true;
404 }
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000405 StringRef Name = *ParseVarResult;
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000406
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000407 IsDefinition = (VarEndIdx != StringRef::npos);
408 if (IsDefinition) {
409 if ((IsPseudo || !MatchStr.consume_front(":"))) {
410 SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
411 SourceMgr::DK_Error,
412 "invalid name in string variable definition");
413 return true;
414 }
415
416 // Detect collisions between string and numeric variables when the
417 // former is created later than the latter.
418 if (Context->GlobalNumericVariableTable.find(Name) !=
419 Context->GlobalNumericVariableTable.end()) {
420 SM.PrintMessage(
421 SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
422 "numeric variable with name '" + Name + "' already exists");
423 return true;
424 }
425 DefName = Name;
426 MatchRegexp = MatchStr;
427 } else {
428 if (IsPseudo) {
429 MatchStr = OrigMatchStr;
430 IsNumBlock = true;
431 } else
432 SubstStr = Name;
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000433 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000434 }
435
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000436 // Parse numeric substitution block.
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000437 FileCheckExpression *Expression;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000438 Optional<FileCheckNumericVariable *> DefinedNumericVariable;
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000439 if (IsNumBlock) {
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000440 Expected<FileCheckExpression *> ParseResult =
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000441 parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM);
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000442 if (!ParseResult) {
443 logAllUnhandledErrors(ParseResult.takeError(), errs());
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000444 return true;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000445 }
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000446 Expression = *ParseResult;
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000447 if (DefinedNumericVariable) {
448 IsDefinition = true;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000449 DefName = (*DefinedNumericVariable)->getName();
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000450 MatchRegexp = StringRef("[0-9]+");
451 } else
452 SubstStr = MatchStr;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000453 }
454
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000455 // Handle substitutions: [[foo]] and [[#<foo expr>]].
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000456 if (!IsDefinition) {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000457 // Handle substitution of string variables that were defined earlier on
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000458 // the same line by emitting a backreference. Expressions do not
459 // support substituting a numeric variable defined on the same line.
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000460 if (!IsNumBlock && VariableDefs.find(SubstStr) != VariableDefs.end()) {
461 unsigned CaptureParenGroup = VariableDefs[SubstStr];
462 if (CaptureParenGroup < 1 || CaptureParenGroup > 9) {
463 SM.PrintMessage(SMLoc::getFromPointer(SubstStr.data()),
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000464 SourceMgr::DK_Error,
465 "Can't back-reference more than 9 variables");
466 return true;
467 }
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000468 AddBackrefToRegEx(CaptureParenGroup);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000469 } else {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000470 // Handle substitution of string variables ([[<var>]]) defined in
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000471 // previous CHECK patterns, and substitution of expressions.
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000472 FileCheckSubstitution *Substitution =
473 IsNumBlock
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000474 ? Context->makeNumericSubstitution(SubstStr, Expression,
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000475 SubstInsertIdx)
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000476 : Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000477 Substitutions.push_back(Substitution);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000478 }
479 continue;
480 }
481
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000482 // Handle variable definitions: [[<def>:(...)]] and
483 // [[#(...)<def>:(...)]].
484 if (IsNumBlock) {
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000485 FileCheckNumericVariableMatch NumericVariableDefinition = {
486 *DefinedNumericVariable, CurParen};
487 NumericVariableDefs[DefName] = NumericVariableDefinition;
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000488 // This store is done here rather than in match() to allow
489 // parseNumericVariableUse() to get the pointer to the class instance
490 // of the right variable definition corresponding to a given numeric
491 // variable use.
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000492 Context->GlobalNumericVariableTable[DefName] = *DefinedNumericVariable;
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000493 } else {
494 VariableDefs[DefName] = CurParen;
495 // Mark the string variable as defined to detect collisions between
496 // string and numeric variables in parseNumericVariableUse() and
497 // DefineCmdlineVariables() when the latter is created later than the
498 // former. We cannot reuse GlobalVariableTable for this by populating
499 // it with an empty string since we would then lose the ability to
500 // detect the use of an undefined variable in match().
501 Context->DefinedVariableTable[DefName] = true;
502 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000503 RegExStr += '(';
504 ++CurParen;
505
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000506 if (AddRegExToRegEx(MatchRegexp, CurParen, SM))
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000507 return true;
508
509 RegExStr += ')';
510 }
511
512 // Handle fixed string matches.
513 // Find the end, which is the start of the next regex.
514 size_t FixedMatchEnd = PatternStr.find("{{");
515 FixedMatchEnd = std::min(FixedMatchEnd, PatternStr.find("[["));
516 RegExStr += Regex::escape(PatternStr.substr(0, FixedMatchEnd));
517 PatternStr = PatternStr.substr(FixedMatchEnd);
518 }
519
520 if (MatchFullLinesHere) {
521 if (!Req.NoCanonicalizeWhiteSpace)
522 RegExStr += " *";
523 RegExStr += '$';
524 }
525
526 return false;
527}
528
529bool FileCheckPattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM) {
530 Regex R(RS);
531 std::string Error;
532 if (!R.isValid(Error)) {
533 SM.PrintMessage(SMLoc::getFromPointer(RS.data()), SourceMgr::DK_Error,
534 "invalid regex: " + Error);
535 return true;
536 }
537
538 RegExStr += RS.str();
539 CurParen += R.getNumMatches();
540 return false;
541}
542
543void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) {
544 assert(BackrefNum >= 1 && BackrefNum <= 9 && "Invalid backref number");
545 std::string Backref = std::string("\\") + std::string(1, '0' + BackrefNum);
546 RegExStr += Backref;
547}
548
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000549Expected<size_t> FileCheckPattern::match(StringRef Buffer, size_t &MatchLen,
550 const SourceMgr &SM) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000551 // If this is the EOF pattern, match it immediately.
552 if (CheckTy == Check::CheckEOF) {
553 MatchLen = 0;
554 return Buffer.size();
555 }
556
557 // If this is a fixed string pattern, just match it now.
558 if (!FixedStr.empty()) {
559 MatchLen = FixedStr.size();
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000560 size_t Pos = Buffer.find(FixedStr);
561 if (Pos == StringRef::npos)
562 return make_error<FileCheckNotFoundError>();
563 return Pos;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000564 }
565
566 // Regex match.
567
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000568 // If there are substitutions, we need to create a temporary string with the
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000569 // actual value.
570 StringRef RegExToMatch = RegExStr;
571 std::string TmpStr;
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000572 if (!Substitutions.empty()) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000573 TmpStr = RegExStr;
Thomas Preud'homme56f63082019-07-05 16:25:46 +0000574 Context->LineVariable->setValue(LineNumber);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000575
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000576 size_t InsertOffset = 0;
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000577 // Substitute all string variables and expressions whose values are only
578 // now known. Use of string variables defined on the same line are handled
579 // by back-references.
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000580 for (const auto &Substitution : Substitutions) {
581 // Substitute and check for failure (e.g. use of undefined variable).
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000582 Expected<std::string> Value = Substitution->getResult();
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000583 if (!Value)
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000584 return Value.takeError();
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000585
586 // Plop it into the regex at the adjusted offset.
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000587 TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset,
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000588 Value->begin(), Value->end());
589 InsertOffset += Value->size();
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000590 }
591
592 // Match the newly constructed regex.
593 RegExToMatch = TmpStr;
Thomas Preud'homme56f63082019-07-05 16:25:46 +0000594 Context->LineVariable->clearValue();
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000595 }
596
597 SmallVector<StringRef, 4> MatchInfo;
598 if (!Regex(RegExToMatch, Regex::Newline).match(Buffer, &MatchInfo))
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000599 return make_error<FileCheckNotFoundError>();
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000600
601 // Successful regex match.
602 assert(!MatchInfo.empty() && "Didn't get any match");
603 StringRef FullMatch = MatchInfo[0];
604
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000605 // If this defines any string variables, remember their values.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000606 for (const auto &VariableDef : VariableDefs) {
607 assert(VariableDef.second < MatchInfo.size() && "Internal paren error");
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000608 Context->GlobalVariableTable[VariableDef.first] =
609 MatchInfo[VariableDef.second];
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000610 }
611
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000612 // If this defines any numeric variables, remember their values.
613 for (const auto &NumericVariableDef : NumericVariableDefs) {
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000614 const FileCheckNumericVariableMatch &NumericVariableMatch =
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000615 NumericVariableDef.getValue();
616 unsigned CaptureParenGroup = NumericVariableMatch.CaptureParenGroup;
617 assert(CaptureParenGroup < MatchInfo.size() && "Internal paren error");
618 FileCheckNumericVariable *DefinedNumericVariable =
619 NumericVariableMatch.DefinedNumericVariable;
620
621 StringRef MatchedValue = MatchInfo[CaptureParenGroup];
622 uint64_t Val;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000623 if (MatchedValue.getAsInteger(10, Val))
624 return FileCheckErrorDiagnostic::get(SM, MatchedValue,
625 "Unable to represent numeric value");
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000626 if (DefinedNumericVariable->setValue(Val))
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000627 llvm_unreachable("Numeric variable redefined");
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000628 }
629
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000630 // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
631 // the required preceding newline, which is consumed by the pattern in the
632 // case of CHECK-EMPTY but not CHECK-NEXT.
633 size_t MatchStartSkip = CheckTy == Check::CheckEmpty;
634 MatchLen = FullMatch.size() - MatchStartSkip;
635 return FullMatch.data() - Buffer.data() + MatchStartSkip;
636}
637
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000638unsigned FileCheckPattern::computeMatchDistance(StringRef Buffer) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000639 // Just compute the number of matching characters. For regular expressions, we
640 // just compare against the regex itself and hope for the best.
641 //
642 // FIXME: One easy improvement here is have the regex lib generate a single
643 // example regular expression which matches, and use that as the example
644 // string.
645 StringRef ExampleString(FixedStr);
646 if (ExampleString.empty())
647 ExampleString = RegExStr;
648
649 // Only compare up to the first line in the buffer, or the string size.
650 StringRef BufferPrefix = Buffer.substr(0, ExampleString.size());
651 BufferPrefix = BufferPrefix.split('\n').first;
652 return BufferPrefix.edit_distance(ExampleString);
653}
654
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000655void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
656 SMRange MatchRange) const {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000657 // Print what we know about substitutions.
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000658 if (!Substitutions.empty()) {
659 for (const auto &Substitution : Substitutions) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000660 SmallString<256> Msg;
661 raw_svector_ostream OS(Msg);
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000662 Expected<std::string> MatchedValue = Substitution->getResult();
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000663
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000664 // Substitution failed or is not known at match time, print the undefined
665 // variable it uses.
666 if (!MatchedValue) {
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000667 bool UndefSeen = false;
668 handleAllErrors(MatchedValue.takeError(),
669 [](const FileCheckNotFoundError &E) {},
Thomas Preud'hommea188ad22019-07-05 12:00:56 +0000670 // Handled in PrintNoMatch().
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000671 [](const FileCheckErrorDiagnostic &E) {},
672 [&](const FileCheckUndefVarError &E) {
673 if (!UndefSeen) {
674 OS << "uses undefined variable ";
675 UndefSeen = true;
676 }
677 E.log(OS);
678 },
679 [](const ErrorInfoBase &E) {
680 llvm_unreachable("Unexpected error");
681 });
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000682 } else {
683 // Substitution succeeded. Print substituted value.
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000684 OS << "with \"";
685 OS.write_escaped(Substitution->getFromString()) << "\" equal to \"";
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000686 OS.write_escaped(*MatchedValue) << "\"";
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000687 }
688
689 if (MatchRange.isValid())
690 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, OS.str(),
691 {MatchRange});
692 else
693 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
694 SourceMgr::DK_Note, OS.str());
695 }
696 }
697}
698
Joel E. Denny3c5d2672018-12-18 00:01:39 +0000699static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,
700 const SourceMgr &SM, SMLoc Loc,
701 Check::FileCheckType CheckTy,
702 StringRef Buffer, size_t Pos, size_t Len,
Joel E. Denny7df86962018-12-18 00:03:03 +0000703 std::vector<FileCheckDiag> *Diags,
704 bool AdjustPrevDiag = false) {
Joel E. Denny3c5d2672018-12-18 00:01:39 +0000705 SMLoc Start = SMLoc::getFromPointer(Buffer.data() + Pos);
706 SMLoc End = SMLoc::getFromPointer(Buffer.data() + Pos + Len);
707 SMRange Range(Start, End);
Joel E. Denny96f0e842018-12-18 00:03:36 +0000708 if (Diags) {
Joel E. Denny7df86962018-12-18 00:03:03 +0000709 if (AdjustPrevDiag)
710 Diags->rbegin()->MatchTy = MatchTy;
711 else
712 Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range);
713 }
Joel E. Denny3c5d2672018-12-18 00:01:39 +0000714 return Range;
715}
716
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000717void FileCheckPattern::printFuzzyMatch(
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000718 const SourceMgr &SM, StringRef Buffer,
Joel E. Denny2c007c82018-12-18 00:02:04 +0000719 std::vector<FileCheckDiag> *Diags) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000720 // Attempt to find the closest/best fuzzy match. Usually an error happens
721 // because some string in the output didn't exactly match. In these cases, we
722 // would like to show the user a best guess at what "should have" matched, to
723 // save them having to actually check the input manually.
724 size_t NumLinesForward = 0;
725 size_t Best = StringRef::npos;
726 double BestQuality = 0;
727
728 // Use an arbitrary 4k limit on how far we will search.
729 for (size_t i = 0, e = std::min(size_t(4096), Buffer.size()); i != e; ++i) {
730 if (Buffer[i] == '\n')
731 ++NumLinesForward;
732
733 // Patterns have leading whitespace stripped, so skip whitespace when
734 // looking for something which looks like a pattern.
735 if (Buffer[i] == ' ' || Buffer[i] == '\t')
736 continue;
737
738 // Compute the "quality" of this match as an arbitrary combination of the
739 // match distance and the number of lines skipped to get to this match.
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000740 unsigned Distance = computeMatchDistance(Buffer.substr(i));
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000741 double Quality = Distance + (NumLinesForward / 100.);
742
743 if (Quality < BestQuality || Best == StringRef::npos) {
744 Best = i;
745 BestQuality = Quality;
746 }
747 }
748
749 // Print the "possible intended match here" line if we found something
750 // reasonable and not equal to what we showed in the "scanning from here"
751 // line.
752 if (Best && Best != StringRef::npos && BestQuality < 50) {
Joel E. Denny2c007c82018-12-18 00:02:04 +0000753 SMRange MatchRange =
754 ProcessMatchResult(FileCheckDiag::MatchFuzzy, SM, getLoc(),
755 getCheckTy(), Buffer, Best, 0, Diags);
756 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note,
757 "possible intended match here");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000758
759 // FIXME: If we wanted to be really friendly we would show why the match
760 // failed, as it can be hard to spot simple one character differences.
761 }
762}
763
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000764Expected<StringRef>
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000765FileCheckPatternContext::getPatternVarValue(StringRef VarName) {
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000766 auto VarIter = GlobalVariableTable.find(VarName);
767 if (VarIter == GlobalVariableTable.end())
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +0000768 return make_error<FileCheckUndefVarError>(VarName);
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000769
770 return VarIter->second;
771}
772
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000773FileCheckExpression *
774FileCheckPatternContext::makeExpression(binop_eval_t EvalBinop,
775 FileCheckNumericVariable *OperandLeft,
776 uint64_t OperandRight) {
777 Expressions.push_back(llvm::make_unique<FileCheckExpression>(
778 EvalBinop, OperandLeft, OperandRight));
779 return Expressions.back().get();
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000780}
781
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000782template <class... Types>
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000783FileCheckNumericVariable *
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000784FileCheckPatternContext::makeNumericVariable(Types... args) {
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000785 NumericVariables.push_back(
Thomas Preud'homme71d3f222019-06-06 13:21:06 +0000786 llvm::make_unique<FileCheckNumericVariable>(args...));
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000787 return NumericVariables.back().get();
788}
789
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000790FileCheckSubstitution *
791FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
792 size_t InsertIdx) {
793 Substitutions.push_back(
794 llvm::make_unique<FileCheckStringSubstitution>(this, VarName, InsertIdx));
795 return Substitutions.back().get();
796}
797
798FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution(
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000799 StringRef ExpressionStr, FileCheckExpression *Expression,
800 size_t InsertIdx) {
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000801 Substitutions.push_back(llvm::make_unique<FileCheckNumericSubstitution>(
Thomas Preud'hommea2ef1ba2019-06-19 23:47:24 +0000802 this, ExpressionStr, Expression, InsertIdx));
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000803 return Substitutions.back().get();
804}
805
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000806size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {
807 // Offset keeps track of the current offset within the input Str
808 size_t Offset = 0;
809 // [...] Nesting depth
810 size_t BracketDepth = 0;
811
812 while (!Str.empty()) {
813 if (Str.startswith("]]") && BracketDepth == 0)
814 return Offset;
815 if (Str[0] == '\\') {
816 // Backslash escapes the next char within regexes, so skip them both.
817 Str = Str.substr(2);
818 Offset += 2;
819 } else {
820 switch (Str[0]) {
821 default:
822 break;
823 case '[':
824 BracketDepth++;
825 break;
826 case ']':
827 if (BracketDepth == 0) {
828 SM.PrintMessage(SMLoc::getFromPointer(Str.data()),
829 SourceMgr::DK_Error,
830 "missing closing \"]\" for regex variable");
831 exit(1);
832 }
833 BracketDepth--;
834 break;
835 }
836 Str = Str.substr(1);
837 Offset++;
838 }
839 }
840
841 return StringRef::npos;
842}
843
Thomas Preud'homme7b7683d2019-05-23 17:19:36 +0000844StringRef FileCheck::CanonicalizeFile(MemoryBuffer &MB,
845 SmallVectorImpl<char> &OutputBuffer) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000846 OutputBuffer.reserve(MB.getBufferSize());
847
848 for (const char *Ptr = MB.getBufferStart(), *End = MB.getBufferEnd();
849 Ptr != End; ++Ptr) {
850 // Eliminate trailing dosish \r.
851 if (Ptr <= End - 2 && Ptr[0] == '\r' && Ptr[1] == '\n') {
852 continue;
853 }
854
855 // If current char is not a horizontal whitespace or if horizontal
856 // whitespace canonicalization is disabled, dump it to output as is.
857 if (Req.NoCanonicalizeWhiteSpace || (*Ptr != ' ' && *Ptr != '\t')) {
858 OutputBuffer.push_back(*Ptr);
859 continue;
860 }
861
862 // Otherwise, add one space and advance over neighboring space.
863 OutputBuffer.push_back(' ');
864 while (Ptr + 1 != End && (Ptr[1] == ' ' || Ptr[1] == '\t'))
865 ++Ptr;
866 }
867
868 // Add a null byte and then return all but that byte.
869 OutputBuffer.push_back('\0');
870 return StringRef(OutputBuffer.data(), OutputBuffer.size() - 1);
871}
872
Joel E. Denny3c5d2672018-12-18 00:01:39 +0000873FileCheckDiag::FileCheckDiag(const SourceMgr &SM,
874 const Check::FileCheckType &CheckTy,
875 SMLoc CheckLoc, MatchType MatchTy,
876 SMRange InputRange)
877 : CheckTy(CheckTy), MatchTy(MatchTy) {
878 auto Start = SM.getLineAndColumn(InputRange.Start);
879 auto End = SM.getLineAndColumn(InputRange.End);
880 InputStartLine = Start.first;
881 InputStartCol = Start.second;
882 InputEndLine = End.first;
883 InputEndCol = End.second;
884 Start = SM.getLineAndColumn(CheckLoc);
885 CheckLine = Start.first;
886 CheckCol = Start.second;
887}
888
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000889static bool IsPartOfWord(char c) {
890 return (isalnum(c) || c == '-' || c == '_');
891}
892
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000893Check::FileCheckType &Check::FileCheckType::setCount(int C) {
Fedor Sergeev8477a3e2018-11-13 01:09:53 +0000894 assert(Count > 0 && "zero and negative counts are not supported");
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000895 assert((C == 1 || Kind == CheckPlain) &&
896 "count supported only for plain CHECK directives");
897 Count = C;
898 return *this;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000899}
900
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000901std::string Check::FileCheckType::getDescription(StringRef Prefix) const {
902 switch (Kind) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000903 case Check::CheckNone:
904 return "invalid";
905 case Check::CheckPlain:
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000906 if (Count > 1)
907 return Prefix.str() + "-COUNT";
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000908 return Prefix;
909 case Check::CheckNext:
910 return Prefix.str() + "-NEXT";
911 case Check::CheckSame:
912 return Prefix.str() + "-SAME";
913 case Check::CheckNot:
914 return Prefix.str() + "-NOT";
915 case Check::CheckDAG:
916 return Prefix.str() + "-DAG";
917 case Check::CheckLabel:
918 return Prefix.str() + "-LABEL";
919 case Check::CheckEmpty:
920 return Prefix.str() + "-EMPTY";
921 case Check::CheckEOF:
922 return "implicit EOF";
923 case Check::CheckBadNot:
924 return "bad NOT";
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000925 case Check::CheckBadCount:
926 return "bad COUNT";
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000927 }
928 llvm_unreachable("unknown FileCheckType");
929}
930
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000931static std::pair<Check::FileCheckType, StringRef>
932FindCheckType(StringRef Buffer, StringRef Prefix) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000933 if (Buffer.size() <= Prefix.size())
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000934 return {Check::CheckNone, StringRef()};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000935
936 char NextChar = Buffer[Prefix.size()];
937
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000938 StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000939 // Verify that the : is present after the prefix.
940 if (NextChar == ':')
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000941 return {Check::CheckPlain, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000942
943 if (NextChar != '-')
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000944 return {Check::CheckNone, StringRef()};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000945
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000946 if (Rest.consume_front("COUNT-")) {
947 int64_t Count;
948 if (Rest.consumeInteger(10, Count))
949 // Error happened in parsing integer.
950 return {Check::CheckBadCount, Rest};
951 if (Count <= 0 || Count > INT32_MAX)
952 return {Check::CheckBadCount, Rest};
953 if (!Rest.consume_front(":"))
954 return {Check::CheckBadCount, Rest};
955 return {Check::FileCheckType(Check::CheckPlain).setCount(Count), Rest};
956 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000957
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000958 if (Rest.consume_front("NEXT:"))
959 return {Check::CheckNext, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000960
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000961 if (Rest.consume_front("SAME:"))
962 return {Check::CheckSame, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000963
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000964 if (Rest.consume_front("NOT:"))
965 return {Check::CheckNot, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000966
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000967 if (Rest.consume_front("DAG:"))
968 return {Check::CheckDAG, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000969
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000970 if (Rest.consume_front("LABEL:"))
971 return {Check::CheckLabel, Rest};
972
973 if (Rest.consume_front("EMPTY:"))
974 return {Check::CheckEmpty, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000975
976 // You can't combine -NOT with another suffix.
977 if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") ||
978 Rest.startswith("NEXT-NOT:") || Rest.startswith("NOT-NEXT:") ||
979 Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:") ||
980 Rest.startswith("EMPTY-NOT:") || Rest.startswith("NOT-EMPTY:"))
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000981 return {Check::CheckBadNot, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000982
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000983 return {Check::CheckNone, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000984}
985
986// From the given position, find the next character after the word.
987static size_t SkipWord(StringRef Str, size_t Loc) {
988 while (Loc < Str.size() && IsPartOfWord(Str[Loc]))
989 ++Loc;
990 return Loc;
991}
992
Thomas Preud'homme4a8ef112019-05-08 21:47:31 +0000993/// Searches the buffer for the first prefix in the prefix regular expression.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000994///
995/// This searches the buffer using the provided regular expression, however it
996/// enforces constraints beyond that:
997/// 1) The found prefix must not be a suffix of something that looks like
998/// a valid prefix.
999/// 2) The found prefix must be followed by a valid check type suffix using \c
1000/// FindCheckType above.
1001///
Thomas Preud'homme4a8ef112019-05-08 21:47:31 +00001002/// \returns a pair of StringRefs into the Buffer, which combines:
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001003/// - the first match of the regular expression to satisfy these two is
1004/// returned,
1005/// otherwise an empty StringRef is returned to indicate failure.
1006/// - buffer rewound to the location right after parsed suffix, for parsing
1007/// to continue from
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001008///
1009/// If this routine returns a valid prefix, it will also shrink \p Buffer to
1010/// start at the beginning of the returned prefix, increment \p LineNumber for
1011/// each new line consumed from \p Buffer, and set \p CheckTy to the type of
1012/// check found by examining the suffix.
1013///
1014/// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy
1015/// is unspecified.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001016static std::pair<StringRef, StringRef>
1017FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
1018 unsigned &LineNumber, Check::FileCheckType &CheckTy) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001019 SmallVector<StringRef, 2> Matches;
1020
1021 while (!Buffer.empty()) {
1022 // Find the first (longest) match using the RE.
1023 if (!PrefixRE.match(Buffer, &Matches))
1024 // No match at all, bail.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001025 return {StringRef(), StringRef()};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001026
1027 StringRef Prefix = Matches[0];
1028 Matches.clear();
1029
1030 assert(Prefix.data() >= Buffer.data() &&
1031 Prefix.data() < Buffer.data() + Buffer.size() &&
1032 "Prefix doesn't start inside of buffer!");
1033 size_t Loc = Prefix.data() - Buffer.data();
1034 StringRef Skipped = Buffer.substr(0, Loc);
1035 Buffer = Buffer.drop_front(Loc);
1036 LineNumber += Skipped.count('\n');
1037
1038 // Check that the matched prefix isn't a suffix of some other check-like
1039 // word.
1040 // FIXME: This is a very ad-hoc check. it would be better handled in some
1041 // other way. Among other things it seems hard to distinguish between
1042 // intentional and unintentional uses of this feature.
1043 if (Skipped.empty() || !IsPartOfWord(Skipped.back())) {
1044 // Now extract the type.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001045 StringRef AfterSuffix;
1046 std::tie(CheckTy, AfterSuffix) = FindCheckType(Buffer, Prefix);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001047
1048 // If we've found a valid check type for this prefix, we're done.
1049 if (CheckTy != Check::CheckNone)
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001050 return {Prefix, AfterSuffix};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001051 }
1052
1053 // If we didn't successfully find a prefix, we need to skip this invalid
1054 // prefix and continue scanning. We directly skip the prefix that was
1055 // matched and any additional parts of that check-like word.
1056 Buffer = Buffer.drop_front(SkipWord(Buffer, Prefix.size()));
1057 }
1058
1059 // We ran out of buffer while skipping partial matches so give up.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001060 return {StringRef(), StringRef()};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001061}
1062
Thomas Preud'homme56f63082019-07-05 16:25:46 +00001063void FileCheckPatternContext::createLineVariable() {
1064 assert(!LineVariable && "@LINE pseudo numeric variable already created");
1065 StringRef LineName = "@LINE";
1066 LineVariable = makeNumericVariable(0, LineName);
1067 GlobalNumericVariableTable[LineName] = LineVariable;
1068}
1069
Thomas Preud'homme7b7683d2019-05-23 17:19:36 +00001070bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
1071 std::vector<FileCheckString> &CheckStrings) {
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001072 Error DefineError =
1073 PatternContext.defineCmdlineVariables(Req.GlobalDefines, SM);
1074 if (DefineError) {
1075 logAllUnhandledErrors(std::move(DefineError), errs());
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001076 return true;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001077 }
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001078
Thomas Preud'homme56f63082019-07-05 16:25:46 +00001079 PatternContext.createLineVariable();
1080
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001081 std::vector<FileCheckPattern> ImplicitNegativeChecks;
1082 for (const auto &PatternString : Req.ImplicitCheckNot) {
1083 // Create a buffer with fake command line content in order to display the
1084 // command line option responsible for the specific implicit CHECK-NOT.
1085 std::string Prefix = "-implicit-check-not='";
1086 std::string Suffix = "'";
1087 std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy(
1088 Prefix + PatternString + Suffix, "command line");
1089
1090 StringRef PatternInBuffer =
1091 CmdLine->getBuffer().substr(Prefix.size(), PatternString.size());
1092 SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
1093
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001094 ImplicitNegativeChecks.push_back(
Thomas Preud'homme71d3f222019-06-06 13:21:06 +00001095 FileCheckPattern(Check::CheckNot, &PatternContext, 0));
1096 ImplicitNegativeChecks.back().parsePattern(PatternInBuffer,
1097 "IMPLICIT-CHECK", SM, Req);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001098 }
1099
1100 std::vector<FileCheckPattern> DagNotMatches = ImplicitNegativeChecks;
1101
1102 // LineNumber keeps track of the line on which CheckPrefix instances are
1103 // found.
1104 unsigned LineNumber = 1;
1105
1106 while (1) {
1107 Check::FileCheckType CheckTy;
1108
1109 // See if a prefix occurs in the memory buffer.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001110 StringRef UsedPrefix;
1111 StringRef AfterSuffix;
1112 std::tie(UsedPrefix, AfterSuffix) =
1113 FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber, CheckTy);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001114 if (UsedPrefix.empty())
1115 break;
1116 assert(UsedPrefix.data() == Buffer.data() &&
1117 "Failed to move Buffer's start forward, or pointed prefix outside "
1118 "of the buffer!");
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001119 assert(AfterSuffix.data() >= Buffer.data() &&
1120 AfterSuffix.data() < Buffer.data() + Buffer.size() &&
1121 "Parsing after suffix doesn't start inside of buffer!");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001122
1123 // Location to use for error messages.
1124 const char *UsedPrefixStart = UsedPrefix.data();
1125
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001126 // Skip the buffer to the end of parsed suffix (or just prefix, if no good
1127 // suffix was processed).
1128 Buffer = AfterSuffix.empty() ? Buffer.drop_front(UsedPrefix.size())
1129 : AfterSuffix;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001130
1131 // Complain about useful-looking but unsupported suffixes.
1132 if (CheckTy == Check::CheckBadNot) {
1133 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,
1134 "unsupported -NOT combo on prefix '" + UsedPrefix + "'");
1135 return true;
1136 }
1137
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001138 // Complain about invalid count specification.
1139 if (CheckTy == Check::CheckBadCount) {
1140 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,
1141 "invalid count in -COUNT specification on prefix '" +
1142 UsedPrefix + "'");
1143 return true;
1144 }
1145
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001146 // Okay, we found the prefix, yay. Remember the rest of the line, but ignore
1147 // leading whitespace.
1148 if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
1149 Buffer = Buffer.substr(Buffer.find_first_not_of(" \t"));
1150
1151 // Scan ahead to the end of line.
1152 size_t EOL = Buffer.find_first_of("\n\r");
1153
1154 // Remember the location of the start of the pattern, for diagnostics.
1155 SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
1156
1157 // Parse the pattern.
Thomas Preud'homme71d3f222019-06-06 13:21:06 +00001158 FileCheckPattern P(CheckTy, &PatternContext, LineNumber);
1159 if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req))
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001160 return true;
1161
1162 // Verify that CHECK-LABEL lines do not define or use variables
1163 if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {
1164 SM.PrintMessage(
1165 SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error,
1166 "found '" + UsedPrefix + "-LABEL:'"
1167 " with variable definition or use");
1168 return true;
1169 }
1170
1171 Buffer = Buffer.substr(EOL);
1172
1173 // Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them.
1174 if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame ||
1175 CheckTy == Check::CheckEmpty) &&
1176 CheckStrings.empty()) {
1177 StringRef Type = CheckTy == Check::CheckNext
1178 ? "NEXT"
1179 : CheckTy == Check::CheckEmpty ? "EMPTY" : "SAME";
1180 SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
1181 SourceMgr::DK_Error,
1182 "found '" + UsedPrefix + "-" + Type +
1183 "' without previous '" + UsedPrefix + ": line");
1184 return true;
1185 }
1186
1187 // Handle CHECK-DAG/-NOT.
1188 if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) {
1189 DagNotMatches.push_back(P);
1190 continue;
1191 }
1192
1193 // Okay, add the string we captured to the output vector and move on.
1194 CheckStrings.emplace_back(P, UsedPrefix, PatternLoc);
1195 std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
1196 DagNotMatches = ImplicitNegativeChecks;
1197 }
1198
1199 // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first
1200 // prefix as a filler for the error message.
1201 if (!DagNotMatches.empty()) {
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001202 CheckStrings.emplace_back(
Thomas Preud'homme71d3f222019-06-06 13:21:06 +00001203 FileCheckPattern(Check::CheckEOF, &PatternContext, LineNumber + 1),
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001204 *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001205 std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
1206 }
1207
1208 if (CheckStrings.empty()) {
1209 errs() << "error: no check strings found with prefix"
1210 << (Req.CheckPrefixes.size() > 1 ? "es " : " ");
1211 auto I = Req.CheckPrefixes.begin();
1212 auto E = Req.CheckPrefixes.end();
1213 if (I != E) {
1214 errs() << "\'" << *I << ":'";
1215 ++I;
1216 }
1217 for (; I != E; ++I)
1218 errs() << ", \'" << *I << ":'";
1219
1220 errs() << '\n';
1221 return true;
1222 }
1223
1224 return false;
1225}
1226
1227static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
1228 StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001229 int MatchedCount, StringRef Buffer, size_t MatchPos,
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001230 size_t MatchLen, const FileCheckRequest &Req,
1231 std::vector<FileCheckDiag> *Diags) {
Joel E. Denny352695c2019-01-22 21:41:42 +00001232 bool PrintDiag = true;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001233 if (ExpectedMatch) {
1234 if (!Req.Verbose)
1235 return;
1236 if (!Req.VerboseVerbose && Pat.getCheckTy() == Check::CheckEOF)
1237 return;
Joel E. Denny352695c2019-01-22 21:41:42 +00001238 // Due to their verbosity, we don't print verbose diagnostics here if we're
1239 // gathering them for a different rendering, but we always print other
1240 // diagnostics.
1241 PrintDiag = !Diags;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001242 }
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001243 SMRange MatchRange = ProcessMatchResult(
Joel E. Dennye2afb612018-12-18 00:03:51 +00001244 ExpectedMatch ? FileCheckDiag::MatchFoundAndExpected
1245 : FileCheckDiag::MatchFoundButExcluded,
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001246 SM, Loc, Pat.getCheckTy(), Buffer, MatchPos, MatchLen, Diags);
Joel E. Denny352695c2019-01-22 21:41:42 +00001247 if (!PrintDiag)
1248 return;
1249
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001250 std::string Message = formatv("{0}: {1} string found in input",
1251 Pat.getCheckTy().getDescription(Prefix),
1252 (ExpectedMatch ? "expected" : "excluded"))
1253 .str();
1254 if (Pat.getCount() > 1)
1255 Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
1256
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001257 SM.PrintMessage(
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001258 Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001259 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
1260 {MatchRange});
Thomas Preud'homme288ed912019-05-02 00:04:38 +00001261 Pat.printSubstitutions(SM, Buffer, MatchRange);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001262}
1263
1264static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001265 const FileCheckString &CheckStr, int MatchedCount,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001266 StringRef Buffer, size_t MatchPos, size_t MatchLen,
1267 FileCheckRequest &Req,
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001268 std::vector<FileCheckDiag> *Diags) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001269 PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001270 MatchedCount, Buffer, MatchPos, MatchLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001271}
1272
1273static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001274 StringRef Prefix, SMLoc Loc,
1275 const FileCheckPattern &Pat, int MatchedCount,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001276 StringRef Buffer, bool VerboseVerbose,
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001277 std::vector<FileCheckDiag> *Diags, Error MatchErrors) {
1278 assert(MatchErrors && "Called on successful match");
Joel E. Denny352695c2019-01-22 21:41:42 +00001279 bool PrintDiag = true;
1280 if (!ExpectedMatch) {
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001281 if (!VerboseVerbose) {
1282 consumeError(std::move(MatchErrors));
Joel E. Denny352695c2019-01-22 21:41:42 +00001283 return;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001284 }
Joel E. Denny352695c2019-01-22 21:41:42 +00001285 // Due to their verbosity, we don't print verbose diagnostics here if we're
1286 // gathering them for a different rendering, but we always print other
1287 // diagnostics.
1288 PrintDiag = !Diags;
1289 }
1290
1291 // If the current position is at the end of a line, advance to the start of
1292 // the next line.
1293 Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r"));
1294 SMRange SearchRange = ProcessMatchResult(
1295 ExpectedMatch ? FileCheckDiag::MatchNoneButExpected
1296 : FileCheckDiag::MatchNoneAndExcluded,
1297 SM, Loc, Pat.getCheckTy(), Buffer, 0, Buffer.size(), Diags);
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001298 if (!PrintDiag) {
1299 consumeError(std::move(MatchErrors));
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001300 return;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001301 }
1302
1303 MatchErrors =
1304 handleErrors(std::move(MatchErrors),
1305 [](const FileCheckErrorDiagnostic &E) { E.log(errs()); });
1306
1307 // No problem matching the string per se.
1308 if (!MatchErrors)
1309 return;
1310 consumeError(std::move(MatchErrors));
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001311
Joel E. Denny352695c2019-01-22 21:41:42 +00001312 // Print "not found" diagnostic.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001313 std::string Message = formatv("{0}: {1} string not found in input",
1314 Pat.getCheckTy().getDescription(Prefix),
1315 (ExpectedMatch ? "expected" : "excluded"))
1316 .str();
1317 if (Pat.getCount() > 1)
1318 Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001319 SM.PrintMessage(
1320 Loc, ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark, Message);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001321
Joel E. Denny352695c2019-01-22 21:41:42 +00001322 // Print the "scanning from here" line.
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001323 SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001324
1325 // Allow the pattern to print additional information if desired.
Thomas Preud'homme288ed912019-05-02 00:04:38 +00001326 Pat.printSubstitutions(SM, Buffer);
Joel E. Denny96f0e842018-12-18 00:03:36 +00001327
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001328 if (ExpectedMatch)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001329 Pat.printFuzzyMatch(SM, Buffer, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001330}
1331
1332static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001333 const FileCheckString &CheckStr, int MatchedCount,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001334 StringRef Buffer, bool VerboseVerbose,
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001335 std::vector<FileCheckDiag> *Diags, Error MatchErrors) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001336 PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001337 MatchedCount, Buffer, VerboseVerbose, Diags,
1338 std::move(MatchErrors));
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001339}
1340
Thomas Preud'homme4a8ef112019-05-08 21:47:31 +00001341/// Counts the number of newlines in the specified range.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001342static unsigned CountNumNewlinesBetween(StringRef Range,
1343 const char *&FirstNewLine) {
1344 unsigned NumNewLines = 0;
1345 while (1) {
1346 // Scan for newline.
1347 Range = Range.substr(Range.find_first_of("\n\r"));
1348 if (Range.empty())
1349 return NumNewLines;
1350
1351 ++NumNewLines;
1352
1353 // Handle \n\r and \r\n as a single newline.
1354 if (Range.size() > 1 && (Range[1] == '\n' || Range[1] == '\r') &&
1355 (Range[0] != Range[1]))
1356 Range = Range.substr(1);
1357 Range = Range.substr(1);
1358
1359 if (NumNewLines == 1)
1360 FirstNewLine = Range.begin();
1361 }
1362}
1363
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001364size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001365 bool IsLabelScanMode, size_t &MatchLen,
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001366 FileCheckRequest &Req,
1367 std::vector<FileCheckDiag> *Diags) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001368 size_t LastPos = 0;
1369 std::vector<const FileCheckPattern *> NotStrings;
1370
1371 // IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL
1372 // bounds; we have not processed variable definitions within the bounded block
1373 // yet so cannot handle any final CHECK-DAG yet; this is handled when going
1374 // over the block again (including the last CHECK-LABEL) in normal mode.
1375 if (!IsLabelScanMode) {
1376 // Match "dag strings" (with mixed "not strings" if any).
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001377 LastPos = CheckDag(SM, Buffer, NotStrings, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001378 if (LastPos == StringRef::npos)
1379 return StringRef::npos;
1380 }
1381
1382 // Match itself from the last position after matching CHECK-DAG.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001383 size_t LastMatchEnd = LastPos;
1384 size_t FirstMatchPos = 0;
1385 // Go match the pattern Count times. Majority of patterns only match with
1386 // count 1 though.
1387 assert(Pat.getCount() != 0 && "pattern count can not be zero");
1388 for (int i = 1; i <= Pat.getCount(); i++) {
1389 StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
1390 size_t CurrentMatchLen;
1391 // get a match at current start point
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001392 Expected<size_t> MatchResult = Pat.match(MatchBuffer, CurrentMatchLen, SM);
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001393
1394 // report
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001395 if (!MatchResult) {
1396 PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags,
1397 MatchResult.takeError());
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001398 return StringRef::npos;
1399 }
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001400 size_t MatchPos = *MatchResult;
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001401 PrintMatch(true, SM, *this, i, MatchBuffer, MatchPos, CurrentMatchLen, Req,
1402 Diags);
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001403 if (i == 1)
1404 FirstMatchPos = LastPos + MatchPos;
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001405
1406 // move start point after the match
1407 LastMatchEnd += MatchPos + CurrentMatchLen;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001408 }
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001409 // Full match len counts from first match pos.
1410 MatchLen = LastMatchEnd - FirstMatchPos;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001411
1412 // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
1413 // or CHECK-NOT
1414 if (!IsLabelScanMode) {
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001415 size_t MatchPos = FirstMatchPos - LastPos;
1416 StringRef MatchBuffer = Buffer.substr(LastPos);
1417 StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001418
1419 // If this check is a "CHECK-NEXT", verify that the previous match was on
1420 // the previous line (i.e. that there is one newline between them).
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001421 if (CheckNext(SM, SkippedRegion)) {
Joel E. Dennye2afb612018-12-18 00:03:51 +00001422 ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001423 Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
Joel E. Denny7df86962018-12-18 00:03:03 +00001424 Diags, Req.Verbose);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001425 return StringRef::npos;
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001426 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001427
1428 // If this check is a "CHECK-SAME", verify that the previous match was on
1429 // the same line (i.e. that there is no newline between them).
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001430 if (CheckSame(SM, SkippedRegion)) {
Joel E. Dennye2afb612018-12-18 00:03:51 +00001431 ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001432 Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
Joel E. Denny7df86962018-12-18 00:03:03 +00001433 Diags, Req.Verbose);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001434 return StringRef::npos;
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001435 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001436
1437 // If this match had "not strings", verify that they don't exist in the
1438 // skipped region.
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001439 if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001440 return StringRef::npos;
1441 }
1442
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001443 return FirstMatchPos;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001444}
1445
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001446bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
1447 if (Pat.getCheckTy() != Check::CheckNext &&
1448 Pat.getCheckTy() != Check::CheckEmpty)
1449 return false;
1450
1451 Twine CheckName =
1452 Prefix +
1453 Twine(Pat.getCheckTy() == Check::CheckEmpty ? "-EMPTY" : "-NEXT");
1454
1455 // Count the number of newlines between the previous match and this one.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001456 const char *FirstNewLine = nullptr;
1457 unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
1458
1459 if (NumNewLines == 0) {
1460 SM.PrintMessage(Loc, SourceMgr::DK_Error,
1461 CheckName + ": is on the same line as previous match");
1462 SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
1463 "'next' match was here");
1464 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
1465 "previous match ended here");
1466 return true;
1467 }
1468
1469 if (NumNewLines != 1) {
1470 SM.PrintMessage(Loc, SourceMgr::DK_Error,
1471 CheckName +
1472 ": is not on the line after the previous match");
1473 SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
1474 "'next' match was here");
1475 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
1476 "previous match ended here");
1477 SM.PrintMessage(SMLoc::getFromPointer(FirstNewLine), SourceMgr::DK_Note,
1478 "non-matching line after previous match is here");
1479 return true;
1480 }
1481
1482 return false;
1483}
1484
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001485bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const {
1486 if (Pat.getCheckTy() != Check::CheckSame)
1487 return false;
1488
1489 // Count the number of newlines between the previous match and this one.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001490 const char *FirstNewLine = nullptr;
1491 unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
1492
1493 if (NumNewLines != 0) {
1494 SM.PrintMessage(Loc, SourceMgr::DK_Error,
1495 Prefix +
1496 "-SAME: is not on the same line as the previous match");
1497 SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
1498 "'next' match was here");
1499 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
1500 "previous match ended here");
1501 return true;
1502 }
1503
1504 return false;
1505}
1506
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001507bool FileCheckString::CheckNot(
1508 const SourceMgr &SM, StringRef Buffer,
1509 const std::vector<const FileCheckPattern *> &NotStrings,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001510 const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001511 for (const FileCheckPattern *Pat : NotStrings) {
1512 assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
1513
1514 size_t MatchLen = 0;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001515 Expected<size_t> MatchResult = Pat->match(Buffer, MatchLen, SM);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001516
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001517 if (!MatchResult) {
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001518 PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001519 Req.VerboseVerbose, Diags, MatchResult.takeError());
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001520 continue;
1521 }
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001522 size_t Pos = *MatchResult;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001523
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001524 PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen,
1525 Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001526
1527 return true;
1528 }
1529
1530 return false;
1531}
1532
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001533size_t
1534FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
1535 std::vector<const FileCheckPattern *> &NotStrings,
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001536 const FileCheckRequest &Req,
1537 std::vector<FileCheckDiag> *Diags) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001538 if (DagNotStrings.empty())
1539 return 0;
1540
1541 // The start of the search range.
1542 size_t StartPos = 0;
1543
1544 struct MatchRange {
1545 size_t Pos;
1546 size_t End;
1547 };
1548 // A sorted list of ranges for non-overlapping CHECK-DAG matches. Match
1549 // ranges are erased from this list once they are no longer in the search
1550 // range.
1551 std::list<MatchRange> MatchRanges;
1552
1553 // We need PatItr and PatEnd later for detecting the end of a CHECK-DAG
1554 // group, so we don't use a range-based for loop here.
1555 for (auto PatItr = DagNotStrings.begin(), PatEnd = DagNotStrings.end();
1556 PatItr != PatEnd; ++PatItr) {
1557 const FileCheckPattern &Pat = *PatItr;
1558 assert((Pat.getCheckTy() == Check::CheckDAG ||
1559 Pat.getCheckTy() == Check::CheckNot) &&
1560 "Invalid CHECK-DAG or CHECK-NOT!");
1561
1562 if (Pat.getCheckTy() == Check::CheckNot) {
1563 NotStrings.push_back(&Pat);
1564 continue;
1565 }
1566
1567 assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!");
1568
1569 // CHECK-DAG always matches from the start.
1570 size_t MatchLen = 0, MatchPos = StartPos;
1571
1572 // Search for a match that doesn't overlap a previous match in this
1573 // CHECK-DAG group.
1574 for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
1575 StringRef MatchBuffer = Buffer.substr(MatchPos);
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001576 Expected<size_t> MatchResult = Pat.match(MatchBuffer, MatchLen, SM);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001577 // With a group of CHECK-DAGs, a single mismatching means the match on
1578 // that group of CHECK-DAGs fails immediately.
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001579 if (!MatchResult) {
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001580 PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer,
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001581 Req.VerboseVerbose, Diags, MatchResult.takeError());
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001582 return StringRef::npos;
1583 }
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001584 size_t MatchPosBuf = *MatchResult;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001585 // Re-calc it as the offset relative to the start of the original string.
1586 MatchPos += MatchPosBuf;
1587 if (Req.VerboseVerbose)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001588 PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
1589 MatchLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001590 MatchRange M{MatchPos, MatchPos + MatchLen};
1591 if (Req.AllowDeprecatedDagOverlap) {
1592 // We don't need to track all matches in this mode, so we just maintain
1593 // one match range that encompasses the current CHECK-DAG group's
1594 // matches.
1595 if (MatchRanges.empty())
1596 MatchRanges.insert(MatchRanges.end(), M);
1597 else {
1598 auto Block = MatchRanges.begin();
1599 Block->Pos = std::min(Block->Pos, M.Pos);
1600 Block->End = std::max(Block->End, M.End);
1601 }
1602 break;
1603 }
1604 // Iterate previous matches until overlapping match or insertion point.
1605 bool Overlap = false;
1606 for (; MI != ME; ++MI) {
1607 if (M.Pos < MI->End) {
1608 // !Overlap => New match has no overlap and is before this old match.
1609 // Overlap => New match overlaps this old match.
1610 Overlap = MI->Pos < M.End;
1611 break;
1612 }
1613 }
1614 if (!Overlap) {
1615 // Insert non-overlapping match into list.
1616 MatchRanges.insert(MI, M);
1617 break;
1618 }
1619 if (Req.VerboseVerbose) {
Joel E. Denny352695c2019-01-22 21:41:42 +00001620 // Due to their verbosity, we don't print verbose diagnostics here if
1621 // we're gathering them for a different rendering, but we always print
1622 // other diagnostics.
1623 if (!Diags) {
1624 SMLoc OldStart = SMLoc::getFromPointer(Buffer.data() + MI->Pos);
1625 SMLoc OldEnd = SMLoc::getFromPointer(Buffer.data() + MI->End);
1626 SMRange OldRange(OldStart, OldEnd);
1627 SM.PrintMessage(OldStart, SourceMgr::DK_Note,
1628 "match discarded, overlaps earlier DAG match here",
1629 {OldRange});
1630 } else
Joel E. Dennye2afb612018-12-18 00:03:51 +00001631 Diags->rbegin()->MatchTy = FileCheckDiag::MatchFoundButDiscarded;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001632 }
1633 MatchPos = MI->End;
1634 }
1635 if (!Req.VerboseVerbose)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001636 PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
1637 MatchLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001638
1639 // Handle the end of a CHECK-DAG group.
1640 if (std::next(PatItr) == PatEnd ||
1641 std::next(PatItr)->getCheckTy() == Check::CheckNot) {
1642 if (!NotStrings.empty()) {
1643 // If there are CHECK-NOTs between two CHECK-DAGs or from CHECK to
1644 // CHECK-DAG, verify that there are no 'not' strings occurred in that
1645 // region.
1646 StringRef SkippedRegion =
1647 Buffer.slice(StartPos, MatchRanges.begin()->Pos);
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001648 if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001649 return StringRef::npos;
1650 // Clear "not strings".
1651 NotStrings.clear();
1652 }
1653 // All subsequent CHECK-DAGs and CHECK-NOTs should be matched from the
1654 // end of this CHECK-DAG group's match range.
1655 StartPos = MatchRanges.rbegin()->End;
1656 // Don't waste time checking for (impossible) overlaps before that.
1657 MatchRanges.clear();
1658 }
1659 }
1660
1661 return StartPos;
1662}
1663
1664// A check prefix must contain only alphanumeric, hyphens and underscores.
1665static bool ValidateCheckPrefix(StringRef CheckPrefix) {
1666 Regex Validator("^[a-zA-Z0-9_-]*$");
1667 return Validator.match(CheckPrefix);
1668}
1669
Thomas Preud'homme7b7683d2019-05-23 17:19:36 +00001670bool FileCheck::ValidateCheckPrefixes() {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001671 StringSet<> PrefixSet;
1672
1673 for (StringRef Prefix : Req.CheckPrefixes) {
1674 // Reject empty prefixes.
1675 if (Prefix == "")
1676 return false;
1677
1678 if (!PrefixSet.insert(Prefix).second)
1679 return false;
1680
1681 if (!ValidateCheckPrefix(Prefix))
1682 return false;
1683 }
1684
1685 return true;
1686}
1687
Thomas Preud'homme7b7683d2019-05-23 17:19:36 +00001688Regex FileCheck::buildCheckPrefixRegex() {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001689 // I don't think there's a way to specify an initial value for cl::list,
1690 // so if nothing was specified, add the default
1691 if (Req.CheckPrefixes.empty())
1692 Req.CheckPrefixes.push_back("CHECK");
1693
1694 // We already validated the contents of CheckPrefixes so just concatenate
1695 // them as alternatives.
1696 SmallString<32> PrefixRegexStr;
1697 for (StringRef Prefix : Req.CheckPrefixes) {
1698 if (Prefix != Req.CheckPrefixes.front())
1699 PrefixRegexStr.push_back('|');
1700
1701 PrefixRegexStr.append(Prefix);
1702 }
1703
1704 return Regex(PrefixRegexStr);
1705}
1706
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001707Error FileCheckPatternContext::defineCmdlineVariables(
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001708 std::vector<std::string> &CmdlineDefines, SourceMgr &SM) {
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001709 assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001710 "Overriding defined variable with command-line variable definitions");
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001711
1712 if (CmdlineDefines.empty())
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001713 return Error::success();
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001714
1715 // Create a string representing the vector of command-line definitions. Each
1716 // definition is on its own line and prefixed with a definition number to
1717 // clarify which definition a given diagnostic corresponds to.
1718 unsigned I = 0;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001719 Error Errs = Error::success();
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001720 std::string CmdlineDefsDiag;
1721 StringRef Prefix1 = "Global define #";
1722 StringRef Prefix2 = ": ";
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001723 for (StringRef CmdlineDef : CmdlineDefines)
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001724 CmdlineDefsDiag +=
1725 (Prefix1 + Twine(++I) + Prefix2 + CmdlineDef + "\n").str();
1726
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001727 // Create a buffer with fake command line content in order to display
1728 // parsing diagnostic with location information and point to the
1729 // global definition with invalid syntax.
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001730 std::unique_ptr<MemoryBuffer> CmdLineDefsDiagBuffer =
1731 MemoryBuffer::getMemBufferCopy(CmdlineDefsDiag, "Global defines");
1732 StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();
1733 SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc());
1734
1735 SmallVector<StringRef, 4> CmdlineDefsDiagVec;
1736 CmdlineDefsDiagRef.split(CmdlineDefsDiagVec, '\n', -1 /*MaxSplit*/,
1737 false /*KeepEmpty*/);
1738 for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) {
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001739 unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
1740 StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
Thomas Preud'homme71d3f222019-06-06 13:21:06 +00001741 size_t EqIdx = CmdlineDef.find('=');
1742 if (EqIdx == StringRef::npos) {
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001743 Errs = joinErrors(
1744 std::move(Errs),
1745 FileCheckErrorDiagnostic::get(
1746 SM, CmdlineDef, "missing equal sign in global definition"));
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001747 continue;
1748 }
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001749
1750 // Numeric variable definition.
1751 if (CmdlineDef[0] == '#') {
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001752 StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
Thomas Preud'homme56f63082019-07-05 16:25:46 +00001753 Expected<FileCheckNumericVariable *> ParseResult =
1754 FileCheckPattern::parseNumericVariableDefinition(CmdlineName, this, 0,
1755 SM);
1756 if (!ParseResult) {
1757 Errs = joinErrors(std::move(Errs), ParseResult.takeError());
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001758 continue;
1759 }
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001760
1761 StringRef CmdlineVal = CmdlineDef.substr(EqIdx + 1);
1762 uint64_t Val;
1763 if (CmdlineVal.getAsInteger(10, Val)) {
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001764 Errs = joinErrors(std::move(Errs),
1765 FileCheckErrorDiagnostic::get(
1766 SM, CmdlineVal,
1767 "invalid value in numeric variable definition '" +
1768 CmdlineVal + "'"));
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001769 continue;
1770 }
Thomas Preud'homme56f63082019-07-05 16:25:46 +00001771 FileCheckNumericVariable *DefinedNumericVariable = *ParseResult;
Thomas Preud'homme71d3f222019-06-06 13:21:06 +00001772 DefinedNumericVariable->setValue(Val);
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001773
1774 // Record this variable definition.
Thomas Preud'homme71d3f222019-06-06 13:21:06 +00001775 GlobalNumericVariableTable[DefinedNumericVariable->getName()] =
1776 DefinedNumericVariable;
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001777 } else {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001778 // String variable definition.
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001779 std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
Thomas Preud'homme71d3f222019-06-06 13:21:06 +00001780 StringRef CmdlineName = CmdlineNameVal.first;
1781 StringRef OrigCmdlineName = CmdlineName;
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001782 bool IsPseudo;
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001783 Expected<StringRef> ParseVarResult =
1784 FileCheckPattern::parseVariable(CmdlineName, IsPseudo, SM);
1785 if (!ParseVarResult) {
1786 Errs = joinErrors(std::move(Errs), ParseVarResult.takeError());
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001787 continue;
1788 }
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001789 // Check that CmdlineName does not denote a pseudo variable is only
1790 // composed of the parsed numeric variable. This catches cases like
1791 // "FOO+2" in a "FOO+2=10" definition.
1792 if (IsPseudo || !CmdlineName.empty()) {
1793 Errs = joinErrors(std::move(Errs),
1794 FileCheckErrorDiagnostic::get(
1795 SM, OrigCmdlineName,
1796 "invalid name in string variable definition '" +
1797 OrigCmdlineName + "'"));
1798 continue;
1799 }
1800 StringRef Name = *ParseVarResult;
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001801
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001802 // Detect collisions between string and numeric variables when the former
1803 // is created later than the latter.
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001804 if (GlobalNumericVariableTable.find(Name) !=
1805 GlobalNumericVariableTable.end()) {
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001806 Errs = joinErrors(std::move(Errs), FileCheckErrorDiagnostic::get(
1807 SM, Name,
1808 "numeric variable with name '" +
1809 Name + "' already exists"));
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001810 continue;
1811 }
1812 GlobalVariableTable.insert(CmdlineNameVal);
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001813 // Mark the string variable as defined to detect collisions between
1814 // string and numeric variables in DefineCmdlineVariables when the latter
1815 // is created later than the former. We cannot reuse GlobalVariableTable
Thomas Preud'homme71d3f222019-06-06 13:21:06 +00001816 // for this by populating it with an empty string since we would then
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001817 // lose the ability to detect the use of an undefined variable in
1818 // match().
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001819 DefinedVariableTable[Name] = true;
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001820 }
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001821 }
1822
Thomas Preud'hommebaae41f2019-06-19 23:47:10 +00001823 return Errs;
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001824}
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001825
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001826void FileCheckPatternContext::clearLocalVars() {
1827 SmallVector<StringRef, 16> LocalPatternVars, LocalNumericVars;
1828 for (const StringMapEntry<StringRef> &Var : GlobalVariableTable)
1829 if (Var.first()[0] != '$')
1830 LocalPatternVars.push_back(Var.first());
1831
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001832 // Numeric substitution reads the value of a variable directly, not via
1833 // GlobalNumericVariableTable. Therefore, we clear local variables by
1834 // clearing their value which will lead to a numeric substitution failure. We
1835 // also mark the variable for removal from GlobalNumericVariableTable since
1836 // this is what defineCmdlineVariables checks to decide that no global
1837 // variable has been defined.
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001838 for (const auto &Var : GlobalNumericVariableTable)
1839 if (Var.first()[0] != '$') {
1840 Var.getValue()->clearValue();
1841 LocalNumericVars.push_back(Var.first());
1842 }
1843
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001844 for (const auto &Var : LocalPatternVars)
1845 GlobalVariableTable.erase(Var);
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001846 for (const auto &Var : LocalNumericVars)
1847 GlobalNumericVariableTable.erase(Var);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001848}
1849
Thomas Preud'homme7b7683d2019-05-23 17:19:36 +00001850bool FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,
1851 ArrayRef<FileCheckString> CheckStrings,
1852 std::vector<FileCheckDiag> *Diags) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001853 bool ChecksFailed = false;
1854
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001855 unsigned i = 0, j = 0, e = CheckStrings.size();
1856 while (true) {
1857 StringRef CheckRegion;
1858 if (j == e) {
1859 CheckRegion = Buffer;
1860 } else {
1861 const FileCheckString &CheckLabelStr = CheckStrings[j];
1862 if (CheckLabelStr.Pat.getCheckTy() != Check::CheckLabel) {
1863 ++j;
1864 continue;
1865 }
1866
1867 // Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG
1868 size_t MatchLabelLen = 0;
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001869 size_t MatchLabelPos =
1870 CheckLabelStr.Check(SM, Buffer, true, MatchLabelLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001871 if (MatchLabelPos == StringRef::npos)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001872 // Immediately bail if CHECK-LABEL fails, nothing else we can do.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001873 return false;
1874
1875 CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen);
1876 Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen);
1877 ++j;
1878 }
1879
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001880 // Do not clear the first region as it's the one before the first
1881 // CHECK-LABEL and it would clear variables defined on the command-line
1882 // before they get used.
1883 if (i != 0 && Req.EnableVarScope)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001884 PatternContext.clearLocalVars();
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001885
1886 for (; i != j; ++i) {
1887 const FileCheckString &CheckStr = CheckStrings[i];
1888
1889 // Check each string within the scanned region, including a second check
1890 // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)
1891 size_t MatchLen = 0;
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001892 size_t MatchPos =
1893 CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001894
1895 if (MatchPos == StringRef::npos) {
1896 ChecksFailed = true;
1897 i = j;
1898 break;
1899 }
1900
1901 CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
1902 }
1903
1904 if (j == e)
1905 break;
1906 }
1907
1908 // Success if no checks failed.
1909 return !ChecksFailed;
1910}