blob: a2b0f84e2c96f4215a1e822f8ee3f17b01528071 [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;
37 Value = llvm::None;
38 return false;
39}
40
41llvm::Optional<uint64_t> FileCheckNumExpr::eval() const {
42 llvm::Optional<uint64_t> LeftOp = this->LeftOp->getValue();
43 // Variable is undefined.
44 if (!LeftOp)
45 return llvm::None;
46 return EvalBinop(*LeftOp, RightOp);
47}
48
49StringRef FileCheckNumExpr::getUndefVarName() const {
50 if (!LeftOp->getValue())
51 return LeftOp->getName();
52 return StringRef();
53}
54
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +000055llvm::Optional<std::string> FileCheckNumericSubstitution::getResult() const {
56 llvm::Optional<uint64_t> EvaluatedValue = NumExpr->eval();
57 if (!EvaluatedValue)
58 return llvm::None;
59 return utostr(*EvaluatedValue);
60}
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000061
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +000062llvm::Optional<std::string> FileCheckStringSubstitution::getResult() const {
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +000063 // Look up the value and escape it so that we can put it into the regex.
64 llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
65 if (!VarVal)
66 return llvm::None;
67 return Regex::escape(*VarVal);
Thomas Preud'homme288ed912019-05-02 00:04:38 +000068}
69
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +000070StringRef FileCheckNumericSubstitution::getUndefVarName() const {
71 // Although a use of an undefined numeric variable is detected at parse
72 // time, a numeric variable can be undefined later by ClearLocalVariables.
73 return NumExpr->getUndefVarName();
74}
Thomas Preud'homme288ed912019-05-02 00:04:38 +000075
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +000076StringRef FileCheckStringSubstitution::getUndefVarName() const {
Thomas Preud'homme288ed912019-05-02 00:04:38 +000077 if (!Context->getPatternVarValue(FromStr))
78 return FromStr;
79
80 return StringRef();
81}
82
Thomas Preud'homme5a330472019-04-29 13:32:36 +000083bool FileCheckPattern::isValidVarNameStart(char C) {
84 return C == '_' || isalpha(C);
85}
86
87bool FileCheckPattern::parseVariable(StringRef Str, bool &IsPseudo,
88 unsigned &TrailIdx) {
89 if (Str.empty())
90 return true;
91
92 bool ParsedOneChar = false;
93 unsigned I = 0;
94 IsPseudo = Str[0] == '@';
95
96 // Global vars start with '$'.
97 if (Str[0] == '$' || IsPseudo)
98 ++I;
99
100 for (unsigned E = Str.size(); I != E; ++I) {
101 if (!ParsedOneChar && !isValidVarNameStart(Str[I]))
102 return true;
103
104 // Variable names are composed of alphanumeric characters and underscores.
105 if (Str[I] != '_' && !isalnum(Str[I]))
106 break;
107 ParsedOneChar = true;
108 }
109
110 TrailIdx = I;
111 return false;
112}
113
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000114// StringRef holding all characters considered as horizontal whitespaces by
115// FileCheck input canonicalization.
116StringRef SpaceChars = " \t";
117
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000118// Parsing helper function that strips the first character in S and returns it.
119static char popFront(StringRef &S) {
120 char C = S.front();
121 S = S.drop_front();
122 return C;
123}
124
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000125static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
126 return LeftOp + RightOp;
127}
128static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
129 return LeftOp - RightOp;
130}
131
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000132FileCheckNumExpr *
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000133FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo,
134 StringRef Trailer,
135 const SourceMgr &SM) const {
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000136 if (IsPseudo && !Name.equals("@LINE")) {
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000137 SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000138 "invalid pseudo numeric variable '" + Name + "'");
139 return nullptr;
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000140 }
141
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000142 // This method is indirectly called from ParsePattern for all numeric
143 // variable definitions and uses in the order in which they appear in the
144 // CHECK pattern. For each definition, the pointer to the class instance of
145 // the corresponding numeric variable definition is stored in
146 // GlobalNumericVariableTable. Therefore, the pointer we get below is for the
147 // class instance corresponding to the last definition of this variable use.
148 auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
149 if (VarTableIter == Context->GlobalNumericVariableTable.end()) {
150 SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
151 "using undefined numeric variable '" + Name + "'");
152 return nullptr;
153 }
154
155 FileCheckNumericVariable *LeftOp = VarTableIter->second;
156
157 // Check if this is a supported operation and select a function to perform
158 // it.
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000159 Trailer = Trailer.ltrim(SpaceChars);
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000160 if (Trailer.empty()) {
161 return Context->makeNumExpr(add, LeftOp, 0);
162 }
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000163 SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data());
164 char Operator = popFront(Trailer);
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000165 binop_eval_t EvalBinop;
166 switch (Operator) {
167 case '+':
168 EvalBinop = add;
169 break;
170 case '-':
171 EvalBinop = sub;
172 break;
173 default:
174 SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
175 Twine("unsupported numeric operation '") + Twine(Operator) +
176 "'");
177 return nullptr;
178 }
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000179
180 // Parse right operand.
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000181 Trailer = Trailer.ltrim(SpaceChars);
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000182 if (Trailer.empty()) {
183 SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000184 "missing operand in numeric expression");
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000185 return nullptr;
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000186 }
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000187 uint64_t RightOp;
188 if (Trailer.consumeInteger(10, RightOp)) {
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000189 SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
190 "invalid offset in numeric expression '" + Trailer + "'");
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000191 return nullptr;
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000192 }
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000193 Trailer = Trailer.ltrim(SpaceChars);
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000194 if (!Trailer.empty()) {
195 SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
196 "unexpected characters at end of numeric expression '" +
197 Trailer + "'");
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000198 return nullptr;
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000199 }
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000200
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000201 return Context->makeNumExpr(EvalBinop, LeftOp, RightOp);
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000202}
203
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000204bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000205 SourceMgr &SM, unsigned LineNumber,
206 const FileCheckRequest &Req) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000207 bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
208
209 this->LineNumber = LineNumber;
210 PatternLoc = SMLoc::getFromPointer(PatternStr.data());
211
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000212 // Create fake @LINE pseudo variable definition.
213 StringRef LinePseudo = "@LINE";
214 uint64_t LineNumber64 = LineNumber;
215 FileCheckNumericVariable *LinePseudoVar =
216 Context->makeNumericVariable(LinePseudo, LineNumber64);
217 Context->GlobalNumericVariableTable[LinePseudo] = LinePseudoVar;
218
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000219 if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
220 // Ignore trailing whitespace.
221 while (!PatternStr.empty() &&
222 (PatternStr.back() == ' ' || PatternStr.back() == '\t'))
223 PatternStr = PatternStr.substr(0, PatternStr.size() - 1);
224
225 // Check that there is something on the line.
226 if (PatternStr.empty() && CheckTy != Check::CheckEmpty) {
227 SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
228 "found empty check string with prefix '" + Prefix + ":'");
229 return true;
230 }
231
232 if (!PatternStr.empty() && CheckTy == Check::CheckEmpty) {
233 SM.PrintMessage(
234 PatternLoc, SourceMgr::DK_Error,
235 "found non-empty check string for empty check with prefix '" + Prefix +
236 ":'");
237 return true;
238 }
239
240 if (CheckTy == Check::CheckEmpty) {
241 RegExStr = "(\n$)";
242 return false;
243 }
244
245 // Check to see if this is a fixed string, or if it has regex pieces.
246 if (!MatchFullLinesHere &&
247 (PatternStr.size() < 2 || (PatternStr.find("{{") == StringRef::npos &&
248 PatternStr.find("[[") == StringRef::npos))) {
249 FixedStr = PatternStr;
250 return false;
251 }
252
253 if (MatchFullLinesHere) {
254 RegExStr += '^';
255 if (!Req.NoCanonicalizeWhiteSpace)
256 RegExStr += " *";
257 }
258
259 // Paren value #0 is for the fully matched string. Any new parenthesized
260 // values add from there.
261 unsigned CurParen = 1;
262
263 // Otherwise, there is at least one regex piece. Build up the regex pattern
264 // by escaping scary characters in fixed strings, building up one big regex.
265 while (!PatternStr.empty()) {
266 // RegEx matches.
267 if (PatternStr.startswith("{{")) {
268 // This is the start of a regex match. Scan for the }}.
269 size_t End = PatternStr.find("}}");
270 if (End == StringRef::npos) {
271 SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
272 SourceMgr::DK_Error,
273 "found start of regex string with no end '}}'");
274 return true;
275 }
276
277 // Enclose {{}} patterns in parens just like [[]] even though we're not
278 // capturing the result for any purpose. This is required in case the
279 // expression contains an alternation like: CHECK: abc{{x|z}}def. We
280 // want this to turn into: "abc(x|z)def" not "abcx|zdef".
281 RegExStr += '(';
282 ++CurParen;
283
284 if (AddRegExToRegEx(PatternStr.substr(2, End - 2), CurParen, SM))
285 return true;
286 RegExStr += ')';
287
288 PatternStr = PatternStr.substr(End + 2);
289 continue;
290 }
291
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000292 // String and numeric substitution blocks. String substitution blocks come
293 // in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some
294 // other regex) and assigns it to the string variable 'foo'. The latter
295 // substitutes foo's value. Numeric substitution blocks start with a
296 // '#' sign after the double brackets and only have the substitution form.
297 // Both string and numeric variables must satisfy the regular expression
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000298 // "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch some common
299 // errors.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000300 if (PatternStr.startswith("[[")) {
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000301 StringRef UnparsedPatternStr = PatternStr.substr(2);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000302 // Find the closing bracket pair ending the match. End is going to be an
303 // offset relative to the beginning of the match string.
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000304 size_t End = FindRegexVarEnd(UnparsedPatternStr, SM);
305 StringRef MatchStr = UnparsedPatternStr.substr(0, End);
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000306 bool IsNumBlock = MatchStr.consume_front("#");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000307
308 if (End == StringRef::npos) {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000309 SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
310 SourceMgr::DK_Error,
311 "Invalid substitution block, no ]] found");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000312 return true;
313 }
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000314 // Strip the substitution block we are parsing. End points to the start
315 // of the "]]" closing the expression so account for it in computing the
316 // index of the first unparsed character.
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000317 PatternStr = UnparsedPatternStr.substr(End + 2);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000318
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000319 size_t VarEndIdx = MatchStr.find(":");
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000320 if (IsNumBlock)
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000321 MatchStr = MatchStr.ltrim(SpaceChars);
322 else {
323 size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
324 if (SpacePos != StringRef::npos) {
325 SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
326 SourceMgr::DK_Error, "unexpected whitespace");
327 return true;
328 }
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000329 }
330
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000331 // Get the variable name (e.g. "foo") and verify it is well formed.
Thomas Preud'homme5a330472019-04-29 13:32:36 +0000332 bool IsPseudo;
333 unsigned TrailIdx;
334 if (parseVariable(MatchStr, IsPseudo, TrailIdx)) {
335 SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000336 SourceMgr::DK_Error, "invalid variable name");
Thomas Preud'homme5a330472019-04-29 13:32:36 +0000337 return true;
338 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000339
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000340 size_t SubstInsertIdx = RegExStr.size();
341 FileCheckNumExpr *NumExpr;
342
Thomas Preud'homme5a330472019-04-29 13:32:36 +0000343 StringRef Name = MatchStr.substr(0, TrailIdx);
344 StringRef Trailer = MatchStr.substr(TrailIdx);
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000345 bool IsVarDef = (VarEndIdx != StringRef::npos);
Thomas Preud'homme5a330472019-04-29 13:32:36 +0000346
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000347 if (IsVarDef) {
348 if (IsPseudo || !Trailer.consume_front(":")) {
349 SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
350 SourceMgr::DK_Error,
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000351 "invalid name in string variable definition");
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000352 return true;
353 }
354
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000355 // Detect collisions between string and numeric variables when the
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000356 // former is created later than the latter.
357 if (Context->GlobalNumericVariableTable.find(Name) !=
358 Context->GlobalNumericVariableTable.end()) {
359 SM.PrintMessage(
360 SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error,
361 "numeric variable with name '" + Name + "' already exists");
362 return true;
363 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000364 }
365
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000366 if (IsNumBlock || (!IsVarDef && IsPseudo)) {
367 NumExpr = parseNumericSubstitution(Name, IsPseudo, Trailer, SM);
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000368 if (NumExpr == nullptr)
Thomas Preud'homme15cb1f12019-04-29 17:46:26 +0000369 return true;
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000370 IsNumBlock = true;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000371 }
372
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000373 // Handle substitutions: [[foo]] and [[#<foo expr>]].
Thomas Preud'homme5a330472019-04-29 13:32:36 +0000374 if (!IsVarDef) {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000375 // Handle substitution of string variables that were defined earlier on
376 // the same line by emitting a backreference.
377 if (!IsNumBlock && VariableDefs.find(Name) != VariableDefs.end()) {
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000378 unsigned CaptureParen = VariableDefs[Name];
379 if (CaptureParen < 1 || CaptureParen > 9) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000380 SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
381 SourceMgr::DK_Error,
382 "Can't back-reference more than 9 variables");
383 return true;
384 }
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000385 AddBackrefToRegEx(CaptureParen);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000386 } else {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000387 // Handle substitution of string variables ([[<var>]]) defined in
388 // previous CHECK patterns, and substitution of numeric expressions.
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000389 FileCheckSubstitution *Substitution =
390 IsNumBlock
391 ? Context->makeNumericSubstitution(MatchStr, NumExpr,
392 SubstInsertIdx)
393 : Context->makeStringSubstitution(MatchStr, SubstInsertIdx);
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000394 Substitutions.push_back(Substitution);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000395 }
396 continue;
397 }
398
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000399 // Handle variable definitions: [[foo:.*]].
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000400 VariableDefs[Name] = CurParen;
401 RegExStr += '(';
402 ++CurParen;
403
Thomas Preud'homme5a330472019-04-29 13:32:36 +0000404 if (AddRegExToRegEx(Trailer, CurParen, SM))
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000405 return true;
406
407 RegExStr += ')';
408 }
409
410 // Handle fixed string matches.
411 // Find the end, which is the start of the next regex.
412 size_t FixedMatchEnd = PatternStr.find("{{");
413 FixedMatchEnd = std::min(FixedMatchEnd, PatternStr.find("[["));
414 RegExStr += Regex::escape(PatternStr.substr(0, FixedMatchEnd));
415 PatternStr = PatternStr.substr(FixedMatchEnd);
416 }
417
418 if (MatchFullLinesHere) {
419 if (!Req.NoCanonicalizeWhiteSpace)
420 RegExStr += " *";
421 RegExStr += '$';
422 }
423
424 return false;
425}
426
427bool FileCheckPattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM) {
428 Regex R(RS);
429 std::string Error;
430 if (!R.isValid(Error)) {
431 SM.PrintMessage(SMLoc::getFromPointer(RS.data()), SourceMgr::DK_Error,
432 "invalid regex: " + Error);
433 return true;
434 }
435
436 RegExStr += RS.str();
437 CurParen += R.getNumMatches();
438 return false;
439}
440
441void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) {
442 assert(BackrefNum >= 1 && BackrefNum <= 9 && "Invalid backref number");
443 std::string Backref = std::string("\\") + std::string(1, '0' + BackrefNum);
444 RegExStr += Backref;
445}
446
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000447size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000448 // If this is the EOF pattern, match it immediately.
449 if (CheckTy == Check::CheckEOF) {
450 MatchLen = 0;
451 return Buffer.size();
452 }
453
454 // If this is a fixed string pattern, just match it now.
455 if (!FixedStr.empty()) {
456 MatchLen = FixedStr.size();
457 return Buffer.find(FixedStr);
458 }
459
460 // Regex match.
461
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000462 // If there are substitutions, we need to create a temporary string with the
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000463 // actual value.
464 StringRef RegExToMatch = RegExStr;
465 std::string TmpStr;
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000466 if (!Substitutions.empty()) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000467 TmpStr = RegExStr;
468
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000469 size_t InsertOffset = 0;
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000470 // Substitute all string variables and numeric expressions whose values are
471 // only now known. Use of string variables defined on the same line are
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000472 // handled by back-references.
473 for (const auto &Substitution : Substitutions) {
474 // Substitute and check for failure (e.g. use of undefined variable).
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000475 llvm::Optional<std::string> Value = Substitution->getResult();
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000476 if (!Value)
477 return StringRef::npos;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000478
479 // Plop it into the regex at the adjusted offset.
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000480 TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset,
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000481 Value->begin(), Value->end());
482 InsertOffset += Value->size();
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000483 }
484
485 // Match the newly constructed regex.
486 RegExToMatch = TmpStr;
487 }
488
489 SmallVector<StringRef, 4> MatchInfo;
490 if (!Regex(RegExToMatch, Regex::Newline).match(Buffer, &MatchInfo))
491 return StringRef::npos;
492
493 // Successful regex match.
494 assert(!MatchInfo.empty() && "Didn't get any match");
495 StringRef FullMatch = MatchInfo[0];
496
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000497 // If this defines any string variables, remember their values.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000498 for (const auto &VariableDef : VariableDefs) {
499 assert(VariableDef.second < MatchInfo.size() && "Internal paren error");
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000500 Context->GlobalVariableTable[VariableDef.first] =
501 MatchInfo[VariableDef.second];
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000502 }
503
504 // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
505 // the required preceding newline, which is consumed by the pattern in the
506 // case of CHECK-EMPTY but not CHECK-NEXT.
507 size_t MatchStartSkip = CheckTy == Check::CheckEmpty;
508 MatchLen = FullMatch.size() - MatchStartSkip;
509 return FullMatch.data() - Buffer.data() + MatchStartSkip;
510}
511
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000512unsigned FileCheckPattern::computeMatchDistance(StringRef Buffer) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000513 // Just compute the number of matching characters. For regular expressions, we
514 // just compare against the regex itself and hope for the best.
515 //
516 // FIXME: One easy improvement here is have the regex lib generate a single
517 // example regular expression which matches, and use that as the example
518 // string.
519 StringRef ExampleString(FixedStr);
520 if (ExampleString.empty())
521 ExampleString = RegExStr;
522
523 // Only compare up to the first line in the buffer, or the string size.
524 StringRef BufferPrefix = Buffer.substr(0, ExampleString.size());
525 BufferPrefix = BufferPrefix.split('\n').first;
526 return BufferPrefix.edit_distance(ExampleString);
527}
528
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000529void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
530 SMRange MatchRange) const {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +0000531 // Print what we know about substitutions.
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000532 if (!Substitutions.empty()) {
533 for (const auto &Substitution : Substitutions) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000534 SmallString<256> Msg;
535 raw_svector_ostream OS(Msg);
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000536 llvm::Optional<std::string> MatchedValue = Substitution->getResult();
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000537
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000538 // Substitution failed or is not known at match time, print the undefined
539 // variable it uses.
540 if (!MatchedValue) {
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000541 StringRef UndefVarName = Substitution->getUndefVarName();
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000542 if (UndefVarName.empty())
543 continue;
544 OS << "uses undefined variable \"";
545 OS.write_escaped(UndefVarName) << "\"";
546 } else {
547 // Substitution succeeded. Print substituted value.
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000548 OS << "with \"";
549 OS.write_escaped(Substitution->getFromString()) << "\" equal to \"";
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000550 OS.write_escaped(*MatchedValue) << "\"";
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000551 }
552
553 if (MatchRange.isValid())
554 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, OS.str(),
555 {MatchRange});
556 else
557 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
558 SourceMgr::DK_Note, OS.str());
559 }
560 }
561}
562
Joel E. Denny3c5d2672018-12-18 00:01:39 +0000563static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,
564 const SourceMgr &SM, SMLoc Loc,
565 Check::FileCheckType CheckTy,
566 StringRef Buffer, size_t Pos, size_t Len,
Joel E. Denny7df86962018-12-18 00:03:03 +0000567 std::vector<FileCheckDiag> *Diags,
568 bool AdjustPrevDiag = false) {
Joel E. Denny3c5d2672018-12-18 00:01:39 +0000569 SMLoc Start = SMLoc::getFromPointer(Buffer.data() + Pos);
570 SMLoc End = SMLoc::getFromPointer(Buffer.data() + Pos + Len);
571 SMRange Range(Start, End);
Joel E. Denny96f0e842018-12-18 00:03:36 +0000572 if (Diags) {
Joel E. Denny7df86962018-12-18 00:03:03 +0000573 if (AdjustPrevDiag)
574 Diags->rbegin()->MatchTy = MatchTy;
575 else
576 Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range);
577 }
Joel E. Denny3c5d2672018-12-18 00:01:39 +0000578 return Range;
579}
580
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000581void FileCheckPattern::printFuzzyMatch(
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000582 const SourceMgr &SM, StringRef Buffer,
Joel E. Denny2c007c82018-12-18 00:02:04 +0000583 std::vector<FileCheckDiag> *Diags) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000584 // Attempt to find the closest/best fuzzy match. Usually an error happens
585 // because some string in the output didn't exactly match. In these cases, we
586 // would like to show the user a best guess at what "should have" matched, to
587 // save them having to actually check the input manually.
588 size_t NumLinesForward = 0;
589 size_t Best = StringRef::npos;
590 double BestQuality = 0;
591
592 // Use an arbitrary 4k limit on how far we will search.
593 for (size_t i = 0, e = std::min(size_t(4096), Buffer.size()); i != e; ++i) {
594 if (Buffer[i] == '\n')
595 ++NumLinesForward;
596
597 // Patterns have leading whitespace stripped, so skip whitespace when
598 // looking for something which looks like a pattern.
599 if (Buffer[i] == ' ' || Buffer[i] == '\t')
600 continue;
601
602 // Compute the "quality" of this match as an arbitrary combination of the
603 // match distance and the number of lines skipped to get to this match.
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000604 unsigned Distance = computeMatchDistance(Buffer.substr(i));
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000605 double Quality = Distance + (NumLinesForward / 100.);
606
607 if (Quality < BestQuality || Best == StringRef::npos) {
608 Best = i;
609 BestQuality = Quality;
610 }
611 }
612
613 // Print the "possible intended match here" line if we found something
614 // reasonable and not equal to what we showed in the "scanning from here"
615 // line.
616 if (Best && Best != StringRef::npos && BestQuality < 50) {
Joel E. Denny2c007c82018-12-18 00:02:04 +0000617 SMRange MatchRange =
618 ProcessMatchResult(FileCheckDiag::MatchFuzzy, SM, getLoc(),
619 getCheckTy(), Buffer, Best, 0, Diags);
620 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note,
621 "possible intended match here");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000622
623 // FIXME: If we wanted to be really friendly we would show why the match
624 // failed, as it can be hard to spot simple one character differences.
625 }
626}
627
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000628llvm::Optional<StringRef>
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000629FileCheckPatternContext::getPatternVarValue(StringRef VarName) {
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000630 auto VarIter = GlobalVariableTable.find(VarName);
631 if (VarIter == GlobalVariableTable.end())
632 return llvm::None;
633
634 return VarIter->second;
635}
636
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000637FileCheckNumExpr *
638FileCheckPatternContext::makeNumExpr(binop_eval_t EvalBinop,
639 FileCheckNumericVariable *OperandLeft,
640 uint64_t OperandRight) {
641 NumExprs.push_back(llvm::make_unique<FileCheckNumExpr>(EvalBinop, OperandLeft,
642 OperandRight));
Thomas Preud'homme288ed912019-05-02 00:04:38 +0000643 return NumExprs.back().get();
644}
645
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +0000646FileCheckNumericVariable *
647FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) {
648 NumericVariables.push_back(
649 llvm::make_unique<FileCheckNumericVariable>(Name, Value));
650 return NumericVariables.back().get();
651}
652
Thomas Preud'hommef3b9bb32019-05-23 00:10:29 +0000653FileCheckSubstitution *
654FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
655 size_t InsertIdx) {
656 Substitutions.push_back(
657 llvm::make_unique<FileCheckStringSubstitution>(this, VarName, InsertIdx));
658 return Substitutions.back().get();
659}
660
661FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution(
662 StringRef Expr, FileCheckNumExpr *NumExpr, size_t InsertIdx) {
663 Substitutions.push_back(llvm::make_unique<FileCheckNumericSubstitution>(
664 this, Expr, NumExpr, InsertIdx));
665 return Substitutions.back().get();
666}
667
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000668size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {
669 // Offset keeps track of the current offset within the input Str
670 size_t Offset = 0;
671 // [...] Nesting depth
672 size_t BracketDepth = 0;
673
674 while (!Str.empty()) {
675 if (Str.startswith("]]") && BracketDepth == 0)
676 return Offset;
677 if (Str[0] == '\\') {
678 // Backslash escapes the next char within regexes, so skip them both.
679 Str = Str.substr(2);
680 Offset += 2;
681 } else {
682 switch (Str[0]) {
683 default:
684 break;
685 case '[':
686 BracketDepth++;
687 break;
688 case ']':
689 if (BracketDepth == 0) {
690 SM.PrintMessage(SMLoc::getFromPointer(Str.data()),
691 SourceMgr::DK_Error,
692 "missing closing \"]\" for regex variable");
693 exit(1);
694 }
695 BracketDepth--;
696 break;
697 }
698 Str = Str.substr(1);
699 Offset++;
700 }
701 }
702
703 return StringRef::npos;
704}
705
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000706StringRef
707llvm::FileCheck::CanonicalizeFile(MemoryBuffer &MB,
708 SmallVectorImpl<char> &OutputBuffer) {
709 OutputBuffer.reserve(MB.getBufferSize());
710
711 for (const char *Ptr = MB.getBufferStart(), *End = MB.getBufferEnd();
712 Ptr != End; ++Ptr) {
713 // Eliminate trailing dosish \r.
714 if (Ptr <= End - 2 && Ptr[0] == '\r' && Ptr[1] == '\n') {
715 continue;
716 }
717
718 // If current char is not a horizontal whitespace or if horizontal
719 // whitespace canonicalization is disabled, dump it to output as is.
720 if (Req.NoCanonicalizeWhiteSpace || (*Ptr != ' ' && *Ptr != '\t')) {
721 OutputBuffer.push_back(*Ptr);
722 continue;
723 }
724
725 // Otherwise, add one space and advance over neighboring space.
726 OutputBuffer.push_back(' ');
727 while (Ptr + 1 != End && (Ptr[1] == ' ' || Ptr[1] == '\t'))
728 ++Ptr;
729 }
730
731 // Add a null byte and then return all but that byte.
732 OutputBuffer.push_back('\0');
733 return StringRef(OutputBuffer.data(), OutputBuffer.size() - 1);
734}
735
Joel E. Denny3c5d2672018-12-18 00:01:39 +0000736FileCheckDiag::FileCheckDiag(const SourceMgr &SM,
737 const Check::FileCheckType &CheckTy,
738 SMLoc CheckLoc, MatchType MatchTy,
739 SMRange InputRange)
740 : CheckTy(CheckTy), MatchTy(MatchTy) {
741 auto Start = SM.getLineAndColumn(InputRange.Start);
742 auto End = SM.getLineAndColumn(InputRange.End);
743 InputStartLine = Start.first;
744 InputStartCol = Start.second;
745 InputEndLine = End.first;
746 InputEndCol = End.second;
747 Start = SM.getLineAndColumn(CheckLoc);
748 CheckLine = Start.first;
749 CheckCol = Start.second;
750}
751
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000752static bool IsPartOfWord(char c) {
753 return (isalnum(c) || c == '-' || c == '_');
754}
755
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000756Check::FileCheckType &Check::FileCheckType::setCount(int C) {
Fedor Sergeev8477a3e2018-11-13 01:09:53 +0000757 assert(Count > 0 && "zero and negative counts are not supported");
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000758 assert((C == 1 || Kind == CheckPlain) &&
759 "count supported only for plain CHECK directives");
760 Count = C;
761 return *this;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000762}
763
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000764std::string Check::FileCheckType::getDescription(StringRef Prefix) const {
765 switch (Kind) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000766 case Check::CheckNone:
767 return "invalid";
768 case Check::CheckPlain:
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000769 if (Count > 1)
770 return Prefix.str() + "-COUNT";
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000771 return Prefix;
772 case Check::CheckNext:
773 return Prefix.str() + "-NEXT";
774 case Check::CheckSame:
775 return Prefix.str() + "-SAME";
776 case Check::CheckNot:
777 return Prefix.str() + "-NOT";
778 case Check::CheckDAG:
779 return Prefix.str() + "-DAG";
780 case Check::CheckLabel:
781 return Prefix.str() + "-LABEL";
782 case Check::CheckEmpty:
783 return Prefix.str() + "-EMPTY";
784 case Check::CheckEOF:
785 return "implicit EOF";
786 case Check::CheckBadNot:
787 return "bad NOT";
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000788 case Check::CheckBadCount:
789 return "bad COUNT";
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000790 }
791 llvm_unreachable("unknown FileCheckType");
792}
793
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000794static std::pair<Check::FileCheckType, StringRef>
795FindCheckType(StringRef Buffer, StringRef Prefix) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000796 if (Buffer.size() <= Prefix.size())
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000797 return {Check::CheckNone, StringRef()};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000798
799 char NextChar = Buffer[Prefix.size()];
800
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000801 StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000802 // Verify that the : is present after the prefix.
803 if (NextChar == ':')
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000804 return {Check::CheckPlain, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000805
806 if (NextChar != '-')
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000807 return {Check::CheckNone, StringRef()};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000808
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000809 if (Rest.consume_front("COUNT-")) {
810 int64_t Count;
811 if (Rest.consumeInteger(10, Count))
812 // Error happened in parsing integer.
813 return {Check::CheckBadCount, Rest};
814 if (Count <= 0 || Count > INT32_MAX)
815 return {Check::CheckBadCount, Rest};
816 if (!Rest.consume_front(":"))
817 return {Check::CheckBadCount, Rest};
818 return {Check::FileCheckType(Check::CheckPlain).setCount(Count), Rest};
819 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000820
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000821 if (Rest.consume_front("NEXT:"))
822 return {Check::CheckNext, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000823
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000824 if (Rest.consume_front("SAME:"))
825 return {Check::CheckSame, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000826
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000827 if (Rest.consume_front("NOT:"))
828 return {Check::CheckNot, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000829
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000830 if (Rest.consume_front("DAG:"))
831 return {Check::CheckDAG, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000832
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000833 if (Rest.consume_front("LABEL:"))
834 return {Check::CheckLabel, Rest};
835
836 if (Rest.consume_front("EMPTY:"))
837 return {Check::CheckEmpty, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000838
839 // You can't combine -NOT with another suffix.
840 if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") ||
841 Rest.startswith("NEXT-NOT:") || Rest.startswith("NOT-NEXT:") ||
842 Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:") ||
843 Rest.startswith("EMPTY-NOT:") || Rest.startswith("NOT-EMPTY:"))
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000844 return {Check::CheckBadNot, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000845
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000846 return {Check::CheckNone, Rest};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000847}
848
849// From the given position, find the next character after the word.
850static size_t SkipWord(StringRef Str, size_t Loc) {
851 while (Loc < Str.size() && IsPartOfWord(Str[Loc]))
852 ++Loc;
853 return Loc;
854}
855
Thomas Preud'homme4a8ef112019-05-08 21:47:31 +0000856/// Searches the buffer for the first prefix in the prefix regular expression.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000857///
858/// This searches the buffer using the provided regular expression, however it
859/// enforces constraints beyond that:
860/// 1) The found prefix must not be a suffix of something that looks like
861/// a valid prefix.
862/// 2) The found prefix must be followed by a valid check type suffix using \c
863/// FindCheckType above.
864///
Thomas Preud'homme4a8ef112019-05-08 21:47:31 +0000865/// \returns a pair of StringRefs into the Buffer, which combines:
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000866/// - the first match of the regular expression to satisfy these two is
867/// returned,
868/// otherwise an empty StringRef is returned to indicate failure.
869/// - buffer rewound to the location right after parsed suffix, for parsing
870/// to continue from
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000871///
872/// If this routine returns a valid prefix, it will also shrink \p Buffer to
873/// start at the beginning of the returned prefix, increment \p LineNumber for
874/// each new line consumed from \p Buffer, and set \p CheckTy to the type of
875/// check found by examining the suffix.
876///
877/// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy
878/// is unspecified.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000879static std::pair<StringRef, StringRef>
880FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
881 unsigned &LineNumber, Check::FileCheckType &CheckTy) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000882 SmallVector<StringRef, 2> Matches;
883
884 while (!Buffer.empty()) {
885 // Find the first (longest) match using the RE.
886 if (!PrefixRE.match(Buffer, &Matches))
887 // No match at all, bail.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000888 return {StringRef(), StringRef()};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000889
890 StringRef Prefix = Matches[0];
891 Matches.clear();
892
893 assert(Prefix.data() >= Buffer.data() &&
894 Prefix.data() < Buffer.data() + Buffer.size() &&
895 "Prefix doesn't start inside of buffer!");
896 size_t Loc = Prefix.data() - Buffer.data();
897 StringRef Skipped = Buffer.substr(0, Loc);
898 Buffer = Buffer.drop_front(Loc);
899 LineNumber += Skipped.count('\n');
900
901 // Check that the matched prefix isn't a suffix of some other check-like
902 // word.
903 // FIXME: This is a very ad-hoc check. it would be better handled in some
904 // other way. Among other things it seems hard to distinguish between
905 // intentional and unintentional uses of this feature.
906 if (Skipped.empty() || !IsPartOfWord(Skipped.back())) {
907 // Now extract the type.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000908 StringRef AfterSuffix;
909 std::tie(CheckTy, AfterSuffix) = FindCheckType(Buffer, Prefix);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000910
911 // If we've found a valid check type for this prefix, we're done.
912 if (CheckTy != Check::CheckNone)
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000913 return {Prefix, AfterSuffix};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000914 }
915
916 // If we didn't successfully find a prefix, we need to skip this invalid
917 // prefix and continue scanning. We directly skip the prefix that was
918 // matched and any additional parts of that check-like word.
919 Buffer = Buffer.drop_front(SkipWord(Buffer, Prefix.size()));
920 }
921
922 // We ran out of buffer while skipping partial matches so give up.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000923 return {StringRef(), StringRef()};
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000924}
925
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000926bool llvm::FileCheck::ReadCheckFile(
927 SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
928 std::vector<FileCheckString> &CheckStrings) {
Thomas Preud'homme5a330472019-04-29 13:32:36 +0000929 if (PatternContext.defineCmdlineVariables(Req.GlobalDefines, SM))
930 return true;
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000931
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000932 std::vector<FileCheckPattern> ImplicitNegativeChecks;
933 for (const auto &PatternString : Req.ImplicitCheckNot) {
934 // Create a buffer with fake command line content in order to display the
935 // command line option responsible for the specific implicit CHECK-NOT.
936 std::string Prefix = "-implicit-check-not='";
937 std::string Suffix = "'";
938 std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy(
939 Prefix + PatternString + Suffix, "command line");
940
941 StringRef PatternInBuffer =
942 CmdLine->getBuffer().substr(Prefix.size(), PatternString.size());
943 SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
944
Thomas Preud'hommee038fa72019-04-15 10:10:11 +0000945 ImplicitNegativeChecks.push_back(
946 FileCheckPattern(Check::CheckNot, &PatternContext));
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000947 ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer,
948 "IMPLICIT-CHECK", SM, 0, Req);
949 }
950
951 std::vector<FileCheckPattern> DagNotMatches = ImplicitNegativeChecks;
952
953 // LineNumber keeps track of the line on which CheckPrefix instances are
954 // found.
955 unsigned LineNumber = 1;
956
957 while (1) {
958 Check::FileCheckType CheckTy;
959
960 // See if a prefix occurs in the memory buffer.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000961 StringRef UsedPrefix;
962 StringRef AfterSuffix;
963 std::tie(UsedPrefix, AfterSuffix) =
964 FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber, CheckTy);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000965 if (UsedPrefix.empty())
966 break;
967 assert(UsedPrefix.data() == Buffer.data() &&
968 "Failed to move Buffer's start forward, or pointed prefix outside "
969 "of the buffer!");
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000970 assert(AfterSuffix.data() >= Buffer.data() &&
971 AfterSuffix.data() < Buffer.data() + Buffer.size() &&
972 "Parsing after suffix doesn't start inside of buffer!");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000973
974 // Location to use for error messages.
975 const char *UsedPrefixStart = UsedPrefix.data();
976
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000977 // Skip the buffer to the end of parsed suffix (or just prefix, if no good
978 // suffix was processed).
979 Buffer = AfterSuffix.empty() ? Buffer.drop_front(UsedPrefix.size())
980 : AfterSuffix;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000981
982 // Complain about useful-looking but unsupported suffixes.
983 if (CheckTy == Check::CheckBadNot) {
984 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,
985 "unsupported -NOT combo on prefix '" + UsedPrefix + "'");
986 return true;
987 }
988
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +0000989 // Complain about invalid count specification.
990 if (CheckTy == Check::CheckBadCount) {
991 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,
992 "invalid count in -COUNT specification on prefix '" +
993 UsedPrefix + "'");
994 return true;
995 }
996
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +0000997 // Okay, we found the prefix, yay. Remember the rest of the line, but ignore
998 // leading whitespace.
999 if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
1000 Buffer = Buffer.substr(Buffer.find_first_not_of(" \t"));
1001
1002 // Scan ahead to the end of line.
1003 size_t EOL = Buffer.find_first_of("\n\r");
1004
1005 // Remember the location of the start of the pattern, for diagnostics.
1006 SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
1007
1008 // Parse the pattern.
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001009 FileCheckPattern P(CheckTy, &PatternContext);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001010 if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber, Req))
1011 return true;
1012
1013 // Verify that CHECK-LABEL lines do not define or use variables
1014 if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {
1015 SM.PrintMessage(
1016 SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error,
1017 "found '" + UsedPrefix + "-LABEL:'"
1018 " with variable definition or use");
1019 return true;
1020 }
1021
1022 Buffer = Buffer.substr(EOL);
1023
1024 // Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them.
1025 if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame ||
1026 CheckTy == Check::CheckEmpty) &&
1027 CheckStrings.empty()) {
1028 StringRef Type = CheckTy == Check::CheckNext
1029 ? "NEXT"
1030 : CheckTy == Check::CheckEmpty ? "EMPTY" : "SAME";
1031 SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
1032 SourceMgr::DK_Error,
1033 "found '" + UsedPrefix + "-" + Type +
1034 "' without previous '" + UsedPrefix + ": line");
1035 return true;
1036 }
1037
1038 // Handle CHECK-DAG/-NOT.
1039 if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) {
1040 DagNotMatches.push_back(P);
1041 continue;
1042 }
1043
1044 // Okay, add the string we captured to the output vector and move on.
1045 CheckStrings.emplace_back(P, UsedPrefix, PatternLoc);
1046 std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
1047 DagNotMatches = ImplicitNegativeChecks;
1048 }
1049
1050 // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first
1051 // prefix as a filler for the error message.
1052 if (!DagNotMatches.empty()) {
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001053 CheckStrings.emplace_back(
1054 FileCheckPattern(Check::CheckEOF, &PatternContext),
1055 *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001056 std::swap(DagNotMatches, CheckStrings.back().DagNotStrings);
1057 }
1058
1059 if (CheckStrings.empty()) {
1060 errs() << "error: no check strings found with prefix"
1061 << (Req.CheckPrefixes.size() > 1 ? "es " : " ");
1062 auto I = Req.CheckPrefixes.begin();
1063 auto E = Req.CheckPrefixes.end();
1064 if (I != E) {
1065 errs() << "\'" << *I << ":'";
1066 ++I;
1067 }
1068 for (; I != E; ++I)
1069 errs() << ", \'" << *I << ":'";
1070
1071 errs() << '\n';
1072 return true;
1073 }
1074
1075 return false;
1076}
1077
1078static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
1079 StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001080 int MatchedCount, StringRef Buffer, size_t MatchPos,
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001081 size_t MatchLen, const FileCheckRequest &Req,
1082 std::vector<FileCheckDiag> *Diags) {
Joel E. Denny352695c2019-01-22 21:41:42 +00001083 bool PrintDiag = true;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001084 if (ExpectedMatch) {
1085 if (!Req.Verbose)
1086 return;
1087 if (!Req.VerboseVerbose && Pat.getCheckTy() == Check::CheckEOF)
1088 return;
Joel E. Denny352695c2019-01-22 21:41:42 +00001089 // Due to their verbosity, we don't print verbose diagnostics here if we're
1090 // gathering them for a different rendering, but we always print other
1091 // diagnostics.
1092 PrintDiag = !Diags;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001093 }
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001094 SMRange MatchRange = ProcessMatchResult(
Joel E. Dennye2afb612018-12-18 00:03:51 +00001095 ExpectedMatch ? FileCheckDiag::MatchFoundAndExpected
1096 : FileCheckDiag::MatchFoundButExcluded,
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001097 SM, Loc, Pat.getCheckTy(), Buffer, MatchPos, MatchLen, Diags);
Joel E. Denny352695c2019-01-22 21:41:42 +00001098 if (!PrintDiag)
1099 return;
1100
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001101 std::string Message = formatv("{0}: {1} string found in input",
1102 Pat.getCheckTy().getDescription(Prefix),
1103 (ExpectedMatch ? "expected" : "excluded"))
1104 .str();
1105 if (Pat.getCount() > 1)
1106 Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
1107
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001108 SM.PrintMessage(
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001109 Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001110 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
1111 {MatchRange});
Thomas Preud'homme288ed912019-05-02 00:04:38 +00001112 Pat.printSubstitutions(SM, Buffer, MatchRange);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001113}
1114
1115static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001116 const FileCheckString &CheckStr, int MatchedCount,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001117 StringRef Buffer, size_t MatchPos, size_t MatchLen,
1118 FileCheckRequest &Req,
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001119 std::vector<FileCheckDiag> *Diags) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001120 PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001121 MatchedCount, Buffer, MatchPos, MatchLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001122}
1123
1124static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001125 StringRef Prefix, SMLoc Loc,
1126 const FileCheckPattern &Pat, int MatchedCount,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001127 StringRef Buffer, bool VerboseVerbose,
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001128 std::vector<FileCheckDiag> *Diags) {
Joel E. Denny352695c2019-01-22 21:41:42 +00001129 bool PrintDiag = true;
1130 if (!ExpectedMatch) {
1131 if (!VerboseVerbose)
1132 return;
1133 // Due to their verbosity, we don't print verbose diagnostics here if we're
1134 // gathering them for a different rendering, but we always print other
1135 // diagnostics.
1136 PrintDiag = !Diags;
1137 }
1138
1139 // If the current position is at the end of a line, advance to the start of
1140 // the next line.
1141 Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r"));
1142 SMRange SearchRange = ProcessMatchResult(
1143 ExpectedMatch ? FileCheckDiag::MatchNoneButExpected
1144 : FileCheckDiag::MatchNoneAndExcluded,
1145 SM, Loc, Pat.getCheckTy(), Buffer, 0, Buffer.size(), Diags);
1146 if (!PrintDiag)
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001147 return;
1148
Joel E. Denny352695c2019-01-22 21:41:42 +00001149 // Print "not found" diagnostic.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001150 std::string Message = formatv("{0}: {1} string not found in input",
1151 Pat.getCheckTy().getDescription(Prefix),
1152 (ExpectedMatch ? "expected" : "excluded"))
1153 .str();
1154 if (Pat.getCount() > 1)
1155 Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001156 SM.PrintMessage(
1157 Loc, ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark, Message);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001158
Joel E. Denny352695c2019-01-22 21:41:42 +00001159 // Print the "scanning from here" line.
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001160 SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here");
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001161
1162 // Allow the pattern to print additional information if desired.
Thomas Preud'homme288ed912019-05-02 00:04:38 +00001163 Pat.printSubstitutions(SM, Buffer);
Joel E. Denny96f0e842018-12-18 00:03:36 +00001164
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001165 if (ExpectedMatch)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001166 Pat.printFuzzyMatch(SM, Buffer, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001167}
1168
1169static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001170 const FileCheckString &CheckStr, int MatchedCount,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001171 StringRef Buffer, bool VerboseVerbose,
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001172 std::vector<FileCheckDiag> *Diags) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001173 PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001174 MatchedCount, Buffer, VerboseVerbose, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001175}
1176
Thomas Preud'homme4a8ef112019-05-08 21:47:31 +00001177/// Counts the number of newlines in the specified range.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001178static unsigned CountNumNewlinesBetween(StringRef Range,
1179 const char *&FirstNewLine) {
1180 unsigned NumNewLines = 0;
1181 while (1) {
1182 // Scan for newline.
1183 Range = Range.substr(Range.find_first_of("\n\r"));
1184 if (Range.empty())
1185 return NumNewLines;
1186
1187 ++NumNewLines;
1188
1189 // Handle \n\r and \r\n as a single newline.
1190 if (Range.size() > 1 && (Range[1] == '\n' || Range[1] == '\r') &&
1191 (Range[0] != Range[1]))
1192 Range = Range.substr(1);
1193 Range = Range.substr(1);
1194
1195 if (NumNewLines == 1)
1196 FirstNewLine = Range.begin();
1197 }
1198}
1199
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001200size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001201 bool IsLabelScanMode, size_t &MatchLen,
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001202 FileCheckRequest &Req,
1203 std::vector<FileCheckDiag> *Diags) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001204 size_t LastPos = 0;
1205 std::vector<const FileCheckPattern *> NotStrings;
1206
1207 // IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL
1208 // bounds; we have not processed variable definitions within the bounded block
1209 // yet so cannot handle any final CHECK-DAG yet; this is handled when going
1210 // over the block again (including the last CHECK-LABEL) in normal mode.
1211 if (!IsLabelScanMode) {
1212 // Match "dag strings" (with mixed "not strings" if any).
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001213 LastPos = CheckDag(SM, Buffer, NotStrings, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001214 if (LastPos == StringRef::npos)
1215 return StringRef::npos;
1216 }
1217
1218 // Match itself from the last position after matching CHECK-DAG.
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001219 size_t LastMatchEnd = LastPos;
1220 size_t FirstMatchPos = 0;
1221 // Go match the pattern Count times. Majority of patterns only match with
1222 // count 1 though.
1223 assert(Pat.getCount() != 0 && "pattern count can not be zero");
1224 for (int i = 1; i <= Pat.getCount(); i++) {
1225 StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
1226 size_t CurrentMatchLen;
1227 // get a match at current start point
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001228 size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen);
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001229 if (i == 1)
1230 FirstMatchPos = LastPos + MatchPos;
1231
1232 // report
1233 if (MatchPos == StringRef::npos) {
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001234 PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags);
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001235 return StringRef::npos;
1236 }
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001237 PrintMatch(true, SM, *this, i, MatchBuffer, MatchPos, CurrentMatchLen, Req,
1238 Diags);
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001239
1240 // move start point after the match
1241 LastMatchEnd += MatchPos + CurrentMatchLen;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001242 }
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001243 // Full match len counts from first match pos.
1244 MatchLen = LastMatchEnd - FirstMatchPos;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001245
1246 // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
1247 // or CHECK-NOT
1248 if (!IsLabelScanMode) {
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001249 size_t MatchPos = FirstMatchPos - LastPos;
1250 StringRef MatchBuffer = Buffer.substr(LastPos);
1251 StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001252
1253 // If this check is a "CHECK-NEXT", verify that the previous match was on
1254 // the previous line (i.e. that there is one newline between them).
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001255 if (CheckNext(SM, SkippedRegion)) {
Joel E. Dennye2afb612018-12-18 00:03:51 +00001256 ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001257 Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
Joel E. Denny7df86962018-12-18 00:03:03 +00001258 Diags, Req.Verbose);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001259 return StringRef::npos;
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001260 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001261
1262 // If this check is a "CHECK-SAME", verify that the previous match was on
1263 // the same line (i.e. that there is no newline between them).
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001264 if (CheckSame(SM, SkippedRegion)) {
Joel E. Dennye2afb612018-12-18 00:03:51 +00001265 ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001266 Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
Joel E. Denny7df86962018-12-18 00:03:03 +00001267 Diags, Req.Verbose);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001268 return StringRef::npos;
Joel E. Dennycadfcef2018-12-18 00:02:22 +00001269 }
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001270
1271 // If this match had "not strings", verify that they don't exist in the
1272 // skipped region.
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001273 if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001274 return StringRef::npos;
1275 }
1276
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001277 return FirstMatchPos;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001278}
1279
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001280bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
1281 if (Pat.getCheckTy() != Check::CheckNext &&
1282 Pat.getCheckTy() != Check::CheckEmpty)
1283 return false;
1284
1285 Twine CheckName =
1286 Prefix +
1287 Twine(Pat.getCheckTy() == Check::CheckEmpty ? "-EMPTY" : "-NEXT");
1288
1289 // Count the number of newlines between the previous match and this one.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001290 const char *FirstNewLine = nullptr;
1291 unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
1292
1293 if (NumNewLines == 0) {
1294 SM.PrintMessage(Loc, SourceMgr::DK_Error,
1295 CheckName + ": is on the same line as previous match");
1296 SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
1297 "'next' match was here");
1298 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
1299 "previous match ended here");
1300 return true;
1301 }
1302
1303 if (NumNewLines != 1) {
1304 SM.PrintMessage(Loc, SourceMgr::DK_Error,
1305 CheckName +
1306 ": is not on the line after the previous match");
1307 SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
1308 "'next' match was here");
1309 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
1310 "previous match ended here");
1311 SM.PrintMessage(SMLoc::getFromPointer(FirstNewLine), SourceMgr::DK_Note,
1312 "non-matching line after previous match is here");
1313 return true;
1314 }
1315
1316 return false;
1317}
1318
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001319bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const {
1320 if (Pat.getCheckTy() != Check::CheckSame)
1321 return false;
1322
1323 // Count the number of newlines between the previous match and this one.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001324 const char *FirstNewLine = nullptr;
1325 unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
1326
1327 if (NumNewLines != 0) {
1328 SM.PrintMessage(Loc, SourceMgr::DK_Error,
1329 Prefix +
1330 "-SAME: is not on the same line as the previous match");
1331 SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
1332 "'next' match was here");
1333 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
1334 "previous match ended here");
1335 return true;
1336 }
1337
1338 return false;
1339}
1340
Joel E. Denny0e7e3fa2018-12-18 00:02:47 +00001341bool FileCheckString::CheckNot(
1342 const SourceMgr &SM, StringRef Buffer,
1343 const std::vector<const FileCheckPattern *> &NotStrings,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001344 const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001345 for (const FileCheckPattern *Pat : NotStrings) {
1346 assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
1347
1348 size_t MatchLen = 0;
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001349 size_t Pos = Pat->match(Buffer, MatchLen);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001350
1351 if (Pos == StringRef::npos) {
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001352 PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001353 Req.VerboseVerbose, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001354 continue;
1355 }
1356
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001357 PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen,
1358 Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001359
1360 return true;
1361 }
1362
1363 return false;
1364}
1365
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001366size_t
1367FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
1368 std::vector<const FileCheckPattern *> &NotStrings,
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001369 const FileCheckRequest &Req,
1370 std::vector<FileCheckDiag> *Diags) const {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001371 if (DagNotStrings.empty())
1372 return 0;
1373
1374 // The start of the search range.
1375 size_t StartPos = 0;
1376
1377 struct MatchRange {
1378 size_t Pos;
1379 size_t End;
1380 };
1381 // A sorted list of ranges for non-overlapping CHECK-DAG matches. Match
1382 // ranges are erased from this list once they are no longer in the search
1383 // range.
1384 std::list<MatchRange> MatchRanges;
1385
1386 // We need PatItr and PatEnd later for detecting the end of a CHECK-DAG
1387 // group, so we don't use a range-based for loop here.
1388 for (auto PatItr = DagNotStrings.begin(), PatEnd = DagNotStrings.end();
1389 PatItr != PatEnd; ++PatItr) {
1390 const FileCheckPattern &Pat = *PatItr;
1391 assert((Pat.getCheckTy() == Check::CheckDAG ||
1392 Pat.getCheckTy() == Check::CheckNot) &&
1393 "Invalid CHECK-DAG or CHECK-NOT!");
1394
1395 if (Pat.getCheckTy() == Check::CheckNot) {
1396 NotStrings.push_back(&Pat);
1397 continue;
1398 }
1399
1400 assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!");
1401
1402 // CHECK-DAG always matches from the start.
1403 size_t MatchLen = 0, MatchPos = StartPos;
1404
1405 // Search for a match that doesn't overlap a previous match in this
1406 // CHECK-DAG group.
1407 for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
1408 StringRef MatchBuffer = Buffer.substr(MatchPos);
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001409 size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001410 // With a group of CHECK-DAGs, a single mismatching means the match on
1411 // that group of CHECK-DAGs fails immediately.
1412 if (MatchPosBuf == StringRef::npos) {
Fedor Sergeev6c9e19b2018-11-13 00:46:13 +00001413 PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer,
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001414 Req.VerboseVerbose, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001415 return StringRef::npos;
1416 }
1417 // Re-calc it as the offset relative to the start of the original string.
1418 MatchPos += MatchPosBuf;
1419 if (Req.VerboseVerbose)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001420 PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
1421 MatchLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001422 MatchRange M{MatchPos, MatchPos + MatchLen};
1423 if (Req.AllowDeprecatedDagOverlap) {
1424 // We don't need to track all matches in this mode, so we just maintain
1425 // one match range that encompasses the current CHECK-DAG group's
1426 // matches.
1427 if (MatchRanges.empty())
1428 MatchRanges.insert(MatchRanges.end(), M);
1429 else {
1430 auto Block = MatchRanges.begin();
1431 Block->Pos = std::min(Block->Pos, M.Pos);
1432 Block->End = std::max(Block->End, M.End);
1433 }
1434 break;
1435 }
1436 // Iterate previous matches until overlapping match or insertion point.
1437 bool Overlap = false;
1438 for (; MI != ME; ++MI) {
1439 if (M.Pos < MI->End) {
1440 // !Overlap => New match has no overlap and is before this old match.
1441 // Overlap => New match overlaps this old match.
1442 Overlap = MI->Pos < M.End;
1443 break;
1444 }
1445 }
1446 if (!Overlap) {
1447 // Insert non-overlapping match into list.
1448 MatchRanges.insert(MI, M);
1449 break;
1450 }
1451 if (Req.VerboseVerbose) {
Joel E. Denny352695c2019-01-22 21:41:42 +00001452 // Due to their verbosity, we don't print verbose diagnostics here if
1453 // we're gathering them for a different rendering, but we always print
1454 // other diagnostics.
1455 if (!Diags) {
1456 SMLoc OldStart = SMLoc::getFromPointer(Buffer.data() + MI->Pos);
1457 SMLoc OldEnd = SMLoc::getFromPointer(Buffer.data() + MI->End);
1458 SMRange OldRange(OldStart, OldEnd);
1459 SM.PrintMessage(OldStart, SourceMgr::DK_Note,
1460 "match discarded, overlaps earlier DAG match here",
1461 {OldRange});
1462 } else
Joel E. Dennye2afb612018-12-18 00:03:51 +00001463 Diags->rbegin()->MatchTy = FileCheckDiag::MatchFoundButDiscarded;
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001464 }
1465 MatchPos = MI->End;
1466 }
1467 if (!Req.VerboseVerbose)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001468 PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
1469 MatchLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001470
1471 // Handle the end of a CHECK-DAG group.
1472 if (std::next(PatItr) == PatEnd ||
1473 std::next(PatItr)->getCheckTy() == Check::CheckNot) {
1474 if (!NotStrings.empty()) {
1475 // If there are CHECK-NOTs between two CHECK-DAGs or from CHECK to
1476 // CHECK-DAG, verify that there are no 'not' strings occurred in that
1477 // region.
1478 StringRef SkippedRegion =
1479 Buffer.slice(StartPos, MatchRanges.begin()->Pos);
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001480 if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001481 return StringRef::npos;
1482 // Clear "not strings".
1483 NotStrings.clear();
1484 }
1485 // All subsequent CHECK-DAGs and CHECK-NOTs should be matched from the
1486 // end of this CHECK-DAG group's match range.
1487 StartPos = MatchRanges.rbegin()->End;
1488 // Don't waste time checking for (impossible) overlaps before that.
1489 MatchRanges.clear();
1490 }
1491 }
1492
1493 return StartPos;
1494}
1495
1496// A check prefix must contain only alphanumeric, hyphens and underscores.
1497static bool ValidateCheckPrefix(StringRef CheckPrefix) {
1498 Regex Validator("^[a-zA-Z0-9_-]*$");
1499 return Validator.match(CheckPrefix);
1500}
1501
1502bool llvm::FileCheck::ValidateCheckPrefixes() {
1503 StringSet<> PrefixSet;
1504
1505 for (StringRef Prefix : Req.CheckPrefixes) {
1506 // Reject empty prefixes.
1507 if (Prefix == "")
1508 return false;
1509
1510 if (!PrefixSet.insert(Prefix).second)
1511 return false;
1512
1513 if (!ValidateCheckPrefix(Prefix))
1514 return false;
1515 }
1516
1517 return true;
1518}
1519
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001520Regex llvm::FileCheck::buildCheckPrefixRegex() {
1521 // I don't think there's a way to specify an initial value for cl::list,
1522 // so if nothing was specified, add the default
1523 if (Req.CheckPrefixes.empty())
1524 Req.CheckPrefixes.push_back("CHECK");
1525
1526 // We already validated the contents of CheckPrefixes so just concatenate
1527 // them as alternatives.
1528 SmallString<32> PrefixRegexStr;
1529 for (StringRef Prefix : Req.CheckPrefixes) {
1530 if (Prefix != Req.CheckPrefixes.front())
1531 PrefixRegexStr.push_back('|');
1532
1533 PrefixRegexStr.append(Prefix);
1534 }
1535
1536 return Regex(PrefixRegexStr);
1537}
1538
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001539bool FileCheckPatternContext::defineCmdlineVariables(
1540 std::vector<std::string> &CmdlineDefines, SourceMgr &SM) {
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001541 assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001542 "Overriding defined variable with command-line variable definitions");
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001543
1544 if (CmdlineDefines.empty())
1545 return false;
1546
1547 // Create a string representing the vector of command-line definitions. Each
1548 // definition is on its own line and prefixed with a definition number to
1549 // clarify which definition a given diagnostic corresponds to.
1550 unsigned I = 0;
1551 bool ErrorFound = false;
1552 std::string CmdlineDefsDiag;
1553 StringRef Prefix1 = "Global define #";
1554 StringRef Prefix2 = ": ";
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001555 for (StringRef CmdlineDef : CmdlineDefines)
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001556 CmdlineDefsDiag +=
1557 (Prefix1 + Twine(++I) + Prefix2 + CmdlineDef + "\n").str();
1558
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001559 // Create a buffer with fake command line content in order to display
1560 // parsing diagnostic with location information and point to the
1561 // global definition with invalid syntax.
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001562 std::unique_ptr<MemoryBuffer> CmdLineDefsDiagBuffer =
1563 MemoryBuffer::getMemBufferCopy(CmdlineDefsDiag, "Global defines");
1564 StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();
1565 SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc());
1566
1567 SmallVector<StringRef, 4> CmdlineDefsDiagVec;
1568 CmdlineDefsDiagRef.split(CmdlineDefsDiagVec, '\n', -1 /*MaxSplit*/,
1569 false /*KeepEmpty*/);
1570 for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) {
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001571 unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
1572 StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
1573 if (CmdlineDef.find('=') == StringRef::npos) {
1574 SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()),
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001575 SourceMgr::DK_Error,
1576 "Missing equal sign in global definition");
1577 ErrorFound = true;
1578 continue;
1579 }
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001580
1581 // Numeric variable definition.
1582 if (CmdlineDef[0] == '#') {
1583 bool IsPseudo;
1584 unsigned TrailIdx;
1585 size_t EqIdx = CmdlineDef.find('=');
1586 StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
1587 if (FileCheckPattern::parseVariable(CmdlineName, IsPseudo, TrailIdx) ||
1588 IsPseudo || TrailIdx != CmdlineName.size() || CmdlineName.empty()) {
1589 SM.PrintMessage(SMLoc::getFromPointer(CmdlineName.data()),
1590 SourceMgr::DK_Error,
1591 "invalid name in numeric variable definition '" +
1592 CmdlineName + "'");
1593 ErrorFound = true;
1594 continue;
1595 }
1596
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001597 // Detect collisions between string and numeric variables when the latter
1598 // is created later than the former.
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001599 if (DefinedVariableTable.find(CmdlineName) !=
1600 DefinedVariableTable.end()) {
1601 SM.PrintMessage(
1602 SMLoc::getFromPointer(CmdlineName.data()), SourceMgr::DK_Error,
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001603 "string variable with name '" + CmdlineName + "' already exists");
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001604 ErrorFound = true;
1605 continue;
1606 }
1607
1608 StringRef CmdlineVal = CmdlineDef.substr(EqIdx + 1);
1609 uint64_t Val;
1610 if (CmdlineVal.getAsInteger(10, Val)) {
1611 SM.PrintMessage(SMLoc::getFromPointer(CmdlineVal.data()),
1612 SourceMgr::DK_Error,
1613 "invalid value in numeric variable definition '" +
1614 CmdlineVal + "'");
1615 ErrorFound = true;
1616 continue;
1617 }
1618 auto DefinedNumericVariable = makeNumericVariable(CmdlineName, Val);
1619
1620 // Record this variable definition.
1621 GlobalNumericVariableTable[CmdlineName] = DefinedNumericVariable;
1622 } else {
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001623 // String variable definition.
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001624 std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
1625 StringRef Name = CmdlineNameVal.first;
1626 bool IsPseudo;
1627 unsigned TrailIdx;
1628 if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) ||
1629 IsPseudo || TrailIdx != Name.size() || Name.empty()) {
1630 SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001631 "invalid name in string variable definition '" + Name +
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001632 "'");
1633 ErrorFound = true;
1634 continue;
1635 }
1636
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001637 // Detect collisions between string and numeric variables when the former
1638 // is created later than the latter.
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001639 if (GlobalNumericVariableTable.find(Name) !=
1640 GlobalNumericVariableTable.end()) {
1641 SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
1642 "numeric variable with name '" + Name +
1643 "' already exists");
1644 ErrorFound = true;
1645 continue;
1646 }
1647 GlobalVariableTable.insert(CmdlineNameVal);
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001648 // Mark the string variable as defined to detect collisions between
1649 // string and numeric variables in DefineCmdlineVariables when the latter
1650 // is created later than the former. We cannot reuse GlobalVariableTable
1651 // for that by populating it with an empty string since we would then
1652 // lose the ability to detect the use of an undefined variable in
1653 // match().
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001654 DefinedVariableTable[Name] = true;
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001655 }
Thomas Preud'homme5a330472019-04-29 13:32:36 +00001656 }
1657
1658 return ErrorFound;
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001659}
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001660
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001661void FileCheckPatternContext::clearLocalVars() {
1662 SmallVector<StringRef, 16> LocalPatternVars, LocalNumericVars;
1663 for (const StringMapEntry<StringRef> &Var : GlobalVariableTable)
1664 if (Var.first()[0] != '$')
1665 LocalPatternVars.push_back(Var.first());
1666
Thomas Preud'homme1a944d22019-05-23 00:10:14 +00001667 // Numeric substitution reads the value of a variable directly, not via
1668 // GlobalNumericVariableTable. Therefore, we clear local variables by
1669 // clearing their value which will lead to a numeric substitution failure. We
1670 // also mark the variable for removal from GlobalNumericVariableTable since
1671 // this is what defineCmdlineVariables checks to decide that no global
1672 // variable has been defined.
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001673 for (const auto &Var : GlobalNumericVariableTable)
1674 if (Var.first()[0] != '$') {
1675 Var.getValue()->clearValue();
1676 LocalNumericVars.push_back(Var.first());
1677 }
1678
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001679 for (const auto &Var : LocalPatternVars)
1680 GlobalVariableTable.erase(Var);
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001681 for (const auto &Var : LocalNumericVars)
1682 GlobalNumericVariableTable.erase(Var);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001683}
1684
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001685bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,
Joel E. Denny3c5d2672018-12-18 00:01:39 +00001686 ArrayRef<FileCheckString> CheckStrings,
1687 std::vector<FileCheckDiag> *Diags) {
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001688 bool ChecksFailed = false;
1689
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001690 unsigned i = 0, j = 0, e = CheckStrings.size();
1691 while (true) {
1692 StringRef CheckRegion;
1693 if (j == e) {
1694 CheckRegion = Buffer;
1695 } else {
1696 const FileCheckString &CheckLabelStr = CheckStrings[j];
1697 if (CheckLabelStr.Pat.getCheckTy() != Check::CheckLabel) {
1698 ++j;
1699 continue;
1700 }
1701
1702 // Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG
1703 size_t MatchLabelLen = 0;
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001704 size_t MatchLabelPos =
1705 CheckLabelStr.Check(SM, Buffer, true, MatchLabelLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001706 if (MatchLabelPos == StringRef::npos)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001707 // Immediately bail if CHECK-LABEL fails, nothing else we can do.
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001708 return false;
1709
1710 CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen);
1711 Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen);
1712 ++j;
1713 }
1714
Thomas Preud'homme7b4ecdd2019-05-14 11:58:30 +00001715 // Do not clear the first region as it's the one before the first
1716 // CHECK-LABEL and it would clear variables defined on the command-line
1717 // before they get used.
1718 if (i != 0 && Req.EnableVarScope)
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001719 PatternContext.clearLocalVars();
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001720
1721 for (; i != j; ++i) {
1722 const FileCheckString &CheckStr = CheckStrings[i];
1723
1724 // Check each string within the scanned region, including a second check
1725 // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)
1726 size_t MatchLen = 0;
Thomas Preud'hommee038fa72019-04-15 10:10:11 +00001727 size_t MatchPos =
1728 CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);
Aditya Nandakumarffa9d2e2018-08-07 21:58:49 +00001729
1730 if (MatchPos == StringRef::npos) {
1731 ChecksFailed = true;
1732 i = j;
1733 break;
1734 }
1735
1736 CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
1737 }
1738
1739 if (j == e)
1740 break;
1741 }
1742
1743 // Success if no checks failed.
1744 return !ChecksFailed;
1745}