blob: 23e1ece2113e80f4c7b9ebdff2a083d7f7f606ee [file] [log] [blame]
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001//===- FileCheck.cpp - Check that File's Contents match what is expected --===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// 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 "FileCheckImpl.h"
18#include "llvm/ADT/StringSet.h"
19#include "llvm/ADT/Twine.h"
20#include "llvm/Support/FormatVariadic.h"
21#include <cstdint>
22#include <list>
23#include <tuple>
24#include <utility>
25
26using namespace llvm;
27
Thomas Preud'homme8e966972019-03-05 23:20:29 +000028Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
29 switch (Value) {
30 case Kind::Unsigned:
31 return StringRef("[0-9]+");
32 case Kind::HexUpper:
33 return StringRef("[0-9A-F]+");
34 case Kind::HexLower:
35 return StringRef("[0-9a-f]+");
36 default:
37 return createStringError(std::errc::invalid_argument,
38 "trying to match value with invalid format");
39 }
40}
41
42Expected<std::string>
43ExpressionFormat::getMatchingString(uint64_t IntegerValue) const {
44 switch (Value) {
45 case Kind::Unsigned:
46 return utostr(IntegerValue);
47 case Kind::HexUpper:
48 return utohexstr(IntegerValue, /*LowerCase=*/false);
49 case Kind::HexLower:
50 return utohexstr(IntegerValue, /*LowerCase=*/true);
51 default:
52 return createStringError(std::errc::invalid_argument,
53 "trying to match value with invalid format");
54 }
55}
56
57Expected<uint64_t>
58ExpressionFormat::valueFromStringRepr(StringRef StrVal,
59 const SourceMgr &SM) const {
60 bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
61 uint64_t IntegerValue;
62 if (StrVal.getAsInteger(Hex ? 16 : 10, IntegerValue))
63 return ErrorDiagnostic::get(SM, StrVal,
64 "unable to represent numeric value");
65
66 return IntegerValue;
67}
68
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +000069Expected<uint64_t> NumericVariableUse::eval() const {
70 Optional<uint64_t> Value = Variable->getValue();
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +000071 if (Value)
72 return *Value;
73
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +000074 return make_error<UndefVarError>(Name);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +000075}
76
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +000077Expected<uint64_t> BinaryOperation::eval() const {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +000078 Expected<uint64_t> LeftOp = LeftOperand->eval();
79 Expected<uint64_t> RightOp = RightOperand->eval();
80
81 // Bubble up any error (e.g. undefined variables) in the recursive
82 // evaluation.
83 if (!LeftOp || !RightOp) {
84 Error Err = Error::success();
85 if (!LeftOp)
86 Err = joinErrors(std::move(Err), LeftOp.takeError());
87 if (!RightOp)
88 Err = joinErrors(std::move(Err), RightOp.takeError());
Bill Wendlingc55cf4a2020-02-10 07:06:45 -080089 return std::move(Err);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +000090 }
91
92 return EvalBinop(*LeftOp, *RightOp);
93}
94
Thomas Preud'homme8e966972019-03-05 23:20:29 +000095ExpressionFormat BinaryOperation::getImplicitFormat() const {
96 ExpressionFormat LeftFormat = LeftOperand->getImplicitFormat();
97 ExpressionFormat RightFormat = RightOperand->getImplicitFormat();
98
99 ExpressionFormat Format =
100 LeftFormat != ExpressionFormat::Kind::NoFormat ? LeftFormat : RightFormat;
101 if (LeftFormat != ExpressionFormat::Kind::NoFormat &&
102 RightFormat != ExpressionFormat::Kind::NoFormat &&
103 LeftFormat != RightFormat)
104 Format = ExpressionFormat(ExpressionFormat::Kind::Conflict);
105
106 return Format;
107}
108
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000109Expected<std::string> NumericSubstitution::getResult() const {
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000110 assert(ExpressionPointer->getAST() != nullptr &&
111 "Substituting empty expression");
112 Expected<uint64_t> EvaluatedValue = ExpressionPointer->getAST()->eval();
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000113 if (!EvaluatedValue)
114 return EvaluatedValue.takeError();
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000115 ExpressionFormat Format = ExpressionPointer->getFormat();
116 return Format.getMatchingString(*EvaluatedValue);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000117}
118
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000119Expected<std::string> StringSubstitution::getResult() const {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000120 // Look up the value and escape it so that we can put it into the regex.
121 Expected<StringRef> VarVal = Context->getPatternVarValue(FromStr);
122 if (!VarVal)
123 return VarVal.takeError();
124 return Regex::escape(*VarVal);
125}
126
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000127bool Pattern::isValidVarNameStart(char C) { return C == '_' || isalpha(C); }
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000128
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000129Expected<Pattern::VariableProperties>
130Pattern::parseVariable(StringRef &Str, const SourceMgr &SM) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000131 if (Str.empty())
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000132 return ErrorDiagnostic::get(SM, Str, "empty variable name");
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000133
134 bool ParsedOneChar = false;
135 unsigned I = 0;
136 bool IsPseudo = Str[0] == '@';
137
138 // Global vars start with '$'.
139 if (Str[0] == '$' || IsPseudo)
140 ++I;
141
142 for (unsigned E = Str.size(); I != E; ++I) {
143 if (!ParsedOneChar && !isValidVarNameStart(Str[I]))
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000144 return ErrorDiagnostic::get(SM, Str, "invalid variable name");
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000145
146 // Variable names are composed of alphanumeric characters and underscores.
147 if (Str[I] != '_' && !isalnum(Str[I]))
148 break;
149 ParsedOneChar = true;
150 }
151
152 StringRef Name = Str.take_front(I);
153 Str = Str.substr(I);
154 return VariableProperties {Name, IsPseudo};
155}
156
157// StringRef holding all characters considered as horizontal whitespaces by
158// FileCheck input canonicalization.
159constexpr StringLiteral SpaceChars = " \t";
160
161// Parsing helper function that strips the first character in S and returns it.
162static char popFront(StringRef &S) {
163 char C = S.front();
164 S = S.drop_front();
165 return C;
166}
167
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000168char UndefVarError::ID = 0;
169char ErrorDiagnostic::ID = 0;
170char NotFoundError::ID = 0;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000171
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000172Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000173 StringRef &Expr, FileCheckPatternContext *Context,
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000174 Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
175 const SourceMgr &SM) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000176 Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
177 if (!ParseVarResult)
178 return ParseVarResult.takeError();
179 StringRef Name = ParseVarResult->Name;
180
181 if (ParseVarResult->IsPseudo)
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000182 return ErrorDiagnostic::get(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000183 SM, Name, "definition of pseudo numeric variable unsupported");
184
185 // Detect collisions between string and numeric variables when the latter
186 // is created later than the former.
187 if (Context->DefinedVariableTable.find(Name) !=
188 Context->DefinedVariableTable.end())
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000189 return ErrorDiagnostic::get(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000190 SM, Name, "string variable with name '" + Name + "' already exists");
191
192 Expr = Expr.ltrim(SpaceChars);
193 if (!Expr.empty())
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000194 return ErrorDiagnostic::get(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000195 SM, Expr, "unexpected characters after numeric variable name");
196
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000197 NumericVariable *DefinedNumericVariable;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000198 auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000199 if (VarTableIter != Context->GlobalNumericVariableTable.end()) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000200 DefinedNumericVariable = VarTableIter->second;
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000201 if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat)
202 return ErrorDiagnostic::get(
203 SM, Expr, "format different from previous variable definition");
204 } else
205 DefinedNumericVariable =
206 Context->makeNumericVariable(Name, ImplicitFormat, LineNumber);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000207
208 return DefinedNumericVariable;
209}
210
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000211Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
212 StringRef Name, bool IsPseudo, Optional<size_t> LineNumber,
213 FileCheckPatternContext *Context, const SourceMgr &SM) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000214 if (IsPseudo && !Name.equals("@LINE"))
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000215 return ErrorDiagnostic::get(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000216 SM, Name, "invalid pseudo numeric variable '" + Name + "'");
217
218 // Numeric variable definitions and uses are parsed in the order in which
219 // they appear in the CHECK patterns. For each definition, the pointer to the
220 // class instance of the corresponding numeric variable definition is stored
221 // in GlobalNumericVariableTable in parsePattern. Therefore, if the pointer
222 // we get below is null, it means no such variable was defined before. When
223 // that happens, we create a dummy variable so that parsing can continue. All
224 // uses of undefined variables, whether string or numeric, are then diagnosed
225 // in printSubstitutions() after failing to match.
226 auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000227 NumericVariable *NumericVariable;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000228 if (VarTableIter != Context->GlobalNumericVariableTable.end())
229 NumericVariable = VarTableIter->second;
230 else {
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000231 NumericVariable = Context->makeNumericVariable(
232 Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000233 Context->GlobalNumericVariableTable[Name] = NumericVariable;
234 }
235
236 Optional<size_t> DefLineNumber = NumericVariable->getDefLineNumber();
237 if (DefLineNumber && LineNumber && *DefLineNumber == *LineNumber)
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000238 return ErrorDiagnostic::get(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000239 SM, Name,
240 "numeric variable '" + Name +
241 "' defined earlier in the same CHECK directive");
242
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000243 return std::make_unique<NumericVariableUse>(Name, NumericVariable);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000244}
245
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000246Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
247 StringRef &Expr, AllowedOperand AO, Optional<size_t> LineNumber,
248 FileCheckPatternContext *Context, const SourceMgr &SM) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000249 if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {
250 // Try to parse as a numeric variable use.
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000251 Expected<Pattern::VariableProperties> ParseVarResult =
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000252 parseVariable(Expr, SM);
253 if (ParseVarResult)
254 return parseNumericVariableUse(ParseVarResult->Name,
255 ParseVarResult->IsPseudo, LineNumber,
256 Context, SM);
257 if (AO == AllowedOperand::LineVar)
258 return ParseVarResult.takeError();
259 // Ignore the error and retry parsing as a literal.
260 consumeError(ParseVarResult.takeError());
261 }
262
263 // Otherwise, parse it as a literal.
264 uint64_t LiteralValue;
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000265 if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
266 LiteralValue))
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000267 return std::make_unique<ExpressionLiteral>(LiteralValue);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000268
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000269 return ErrorDiagnostic::get(SM, Expr,
270 "invalid operand format '" + Expr + "'");
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000271}
272
273static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
274 return LeftOp + RightOp;
275}
276
277static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
278 return LeftOp - RightOp;
279}
280
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000281Expected<std::unique_ptr<ExpressionAST>>
282Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
283 bool IsLegacyLineExpr, Optional<size_t> LineNumber,
284 FileCheckPatternContext *Context, const SourceMgr &SM) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000285 Expr = Expr.ltrim(SpaceChars);
286 if (Expr.empty())
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800287 return std::move(LeftOp);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000288
289 // Check if this is a supported operation and select a function to perform
290 // it.
291 SMLoc OpLoc = SMLoc::getFromPointer(Expr.data());
292 char Operator = popFront(Expr);
293 binop_eval_t EvalBinop;
294 switch (Operator) {
295 case '+':
296 EvalBinop = add;
297 break;
298 case '-':
299 EvalBinop = sub;
300 break;
301 default:
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000302 return ErrorDiagnostic::get(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000303 SM, OpLoc, Twine("unsupported operation '") + Twine(Operator) + "'");
304 }
305
306 // Parse right operand.
307 Expr = Expr.ltrim(SpaceChars);
308 if (Expr.empty())
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000309 return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000310 // The second operand in a legacy @LINE expression is always a literal.
311 AllowedOperand AO =
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000312 IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000313 Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000314 parseNumericOperand(Expr, AO, LineNumber, Context, SM);
315 if (!RightOpResult)
316 return RightOpResult;
317
318 Expr = Expr.ltrim(SpaceChars);
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000319 return std::make_unique<BinaryOperation>(EvalBinop, std::move(LeftOp),
320 std::move(*RightOpResult));
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000321}
322
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000323Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000324 StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000325 bool IsLegacyLineExpr, Optional<size_t> LineNumber,
326 FileCheckPatternContext *Context, const SourceMgr &SM) {
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000327 std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000328 StringRef DefExpr = StringRef();
329 DefinedNumericVariable = None;
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000330 ExpressionFormat ExplicitFormat = ExpressionFormat();
331
332 // Parse format specifier.
333 size_t FormatSpecEnd = Expr.find(',');
334 if (FormatSpecEnd != StringRef::npos) {
335 Expr = Expr.ltrim(SpaceChars);
336 if (!Expr.consume_front("%"))
337 return ErrorDiagnostic::get(
338 SM, Expr, "invalid matching format specification in expression");
339
340 // Check for unknown matching format specifier and set matching format in
341 // class instance representing this expression.
342 SMLoc fmtloc = SMLoc::getFromPointer(Expr.data());
343 switch (popFront(Expr)) {
344 case 'u':
345 ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
346 break;
347 case 'x':
348 ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower);
349 break;
350 case 'X':
351 ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper);
352 break;
353 default:
354 return ErrorDiagnostic::get(SM, fmtloc,
355 "invalid format specifier in expression");
356 }
357
358 Expr = Expr.ltrim(SpaceChars);
359 if (!Expr.consume_front(","))
360 return ErrorDiagnostic::get(
361 SM, Expr, "invalid matching format specification in expression");
362 }
363
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000364 // Save variable definition expression if any.
365 size_t DefEnd = Expr.find(':');
366 if (DefEnd != StringRef::npos) {
367 DefExpr = Expr.substr(0, DefEnd);
368 Expr = Expr.substr(DefEnd + 1);
369 }
370
371 // Parse the expression itself.
372 Expr = Expr.ltrim(SpaceChars);
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000373 StringRef UseExpr = Expr;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000374 if (!Expr.empty()) {
375 // The first operand in a legacy @LINE expression is always the @LINE
376 // pseudo variable.
377 AllowedOperand AO =
378 IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000379 Expected<std::unique_ptr<ExpressionAST>> ParseResult =
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000380 parseNumericOperand(Expr, AO, LineNumber, Context, SM);
381 while (ParseResult && !Expr.empty()) {
382 ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr,
383 LineNumber, Context, SM);
384 // Legacy @LINE expressions only allow 2 operands.
385 if (ParseResult && IsLegacyLineExpr && !Expr.empty())
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000386 return ErrorDiagnostic::get(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000387 SM, Expr,
388 "unexpected characters at end of expression '" + Expr + "'");
389 }
390 if (!ParseResult)
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000391 return ParseResult.takeError();
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000392 ExpressionASTPointer = std::move(*ParseResult);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000393 }
394
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000395 // Select format of the expression, i.e. (i) its explicit format, if any,
396 // otherwise (ii) its implicit format, if any, otherwise (iii) the default
397 // format (unsigned). Error out in case of conflicting implicit format
398 // without explicit format.
399 ExpressionFormat Format,
400 ImplicitFormat = ExpressionASTPointer
401 ? ExpressionASTPointer->getImplicitFormat()
402 : ExpressionFormat(ExpressionFormat::Kind::NoFormat);
403 if (bool(ExplicitFormat))
404 Format = ExplicitFormat;
405 else if (ImplicitFormat == ExpressionFormat::Kind::Conflict)
406 return ErrorDiagnostic::get(
407 SM, UseExpr,
408 "variables with conflicting format specifier: need an explicit one");
409 else if (bool(ImplicitFormat))
410 Format = ImplicitFormat;
411 else
412 Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
413
414 std::unique_ptr<Expression> ExpressionPointer =
415 std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);
416
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000417 // Parse the numeric variable definition.
418 if (DefEnd != StringRef::npos) {
419 DefExpr = DefExpr.ltrim(SpaceChars);
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000420 Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition(
421 DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000422
423 if (!ParseResult)
424 return ParseResult.takeError();
425 DefinedNumericVariable = *ParseResult;
426 }
427
Bill Wendlingc55cf4a2020-02-10 07:06:45 -0800428 return std::move(ExpressionPointer);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000429}
430
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000431bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
432 SourceMgr &SM, const FileCheckRequest &Req) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000433 bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
Kai Nacke5b5b2fd2019-10-11 11:59:14 +0000434 IgnoreCase = Req.IgnoreCase;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000435
436 PatternLoc = SMLoc::getFromPointer(PatternStr.data());
437
438 if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
439 // Ignore trailing whitespace.
440 while (!PatternStr.empty() &&
441 (PatternStr.back() == ' ' || PatternStr.back() == '\t'))
442 PatternStr = PatternStr.substr(0, PatternStr.size() - 1);
443
444 // Check that there is something on the line.
445 if (PatternStr.empty() && CheckTy != Check::CheckEmpty) {
446 SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
447 "found empty check string with prefix '" + Prefix + ":'");
448 return true;
449 }
450
451 if (!PatternStr.empty() && CheckTy == Check::CheckEmpty) {
452 SM.PrintMessage(
453 PatternLoc, SourceMgr::DK_Error,
454 "found non-empty check string for empty check with prefix '" + Prefix +
455 ":'");
456 return true;
457 }
458
459 if (CheckTy == Check::CheckEmpty) {
460 RegExStr = "(\n$)";
461 return false;
462 }
463
464 // Check to see if this is a fixed string, or if it has regex pieces.
465 if (!MatchFullLinesHere &&
466 (PatternStr.size() < 2 || (PatternStr.find("{{") == StringRef::npos &&
467 PatternStr.find("[[") == StringRef::npos))) {
468 FixedStr = PatternStr;
469 return false;
470 }
471
472 if (MatchFullLinesHere) {
473 RegExStr += '^';
474 if (!Req.NoCanonicalizeWhiteSpace)
475 RegExStr += " *";
476 }
477
478 // Paren value #0 is for the fully matched string. Any new parenthesized
479 // values add from there.
480 unsigned CurParen = 1;
481
482 // Otherwise, there is at least one regex piece. Build up the regex pattern
483 // by escaping scary characters in fixed strings, building up one big regex.
484 while (!PatternStr.empty()) {
485 // RegEx matches.
486 if (PatternStr.startswith("{{")) {
487 // This is the start of a regex match. Scan for the }}.
488 size_t End = PatternStr.find("}}");
489 if (End == StringRef::npos) {
490 SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
491 SourceMgr::DK_Error,
492 "found start of regex string with no end '}}'");
493 return true;
494 }
495
496 // Enclose {{}} patterns in parens just like [[]] even though we're not
497 // capturing the result for any purpose. This is required in case the
498 // expression contains an alternation like: CHECK: abc{{x|z}}def. We
499 // want this to turn into: "abc(x|z)def" not "abcx|zdef".
500 RegExStr += '(';
501 ++CurParen;
502
503 if (AddRegExToRegEx(PatternStr.substr(2, End - 2), CurParen, SM))
504 return true;
505 RegExStr += ')';
506
507 PatternStr = PatternStr.substr(End + 2);
508 continue;
509 }
510
511 // String and numeric substitution blocks. Pattern substitution blocks come
512 // in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some
513 // other regex) and assigns it to the string variable 'foo'. The latter
514 // substitutes foo's value. Numeric substitution blocks recognize the same
515 // form as string ones, but start with a '#' sign after the double
516 // brackets. They also accept a combined form which sets a numeric variable
517 // to the evaluation of an expression. Both string and numeric variable
518 // names must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*" to be
519 // valid, as this helps catch some common errors.
520 if (PatternStr.startswith("[[")) {
521 StringRef UnparsedPatternStr = PatternStr.substr(2);
522 // Find the closing bracket pair ending the match. End is going to be an
523 // offset relative to the beginning of the match string.
524 size_t End = FindRegexVarEnd(UnparsedPatternStr, SM);
525 StringRef MatchStr = UnparsedPatternStr.substr(0, End);
526 bool IsNumBlock = MatchStr.consume_front("#");
527
528 if (End == StringRef::npos) {
529 SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
530 SourceMgr::DK_Error,
531 "Invalid substitution block, no ]] found");
532 return true;
533 }
534 // Strip the substitution block we are parsing. End points to the start
535 // of the "]]" closing the expression so account for it in computing the
536 // index of the first unparsed character.
537 PatternStr = UnparsedPatternStr.substr(End + 2);
538
539 bool IsDefinition = false;
540 bool SubstNeeded = false;
541 // Whether the substitution block is a legacy use of @LINE with string
542 // substitution block syntax.
543 bool IsLegacyLineExpr = false;
544 StringRef DefName;
545 StringRef SubstStr;
546 StringRef MatchRegexp;
547 size_t SubstInsertIdx = RegExStr.size();
548
549 // Parse string variable or legacy @LINE expression.
550 if (!IsNumBlock) {
551 size_t VarEndIdx = MatchStr.find(":");
552 size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
553 if (SpacePos != StringRef::npos) {
554 SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
555 SourceMgr::DK_Error, "unexpected whitespace");
556 return true;
557 }
558
559 // Get the name (e.g. "foo") and verify it is well formed.
560 StringRef OrigMatchStr = MatchStr;
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000561 Expected<Pattern::VariableProperties> ParseVarResult =
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000562 parseVariable(MatchStr, SM);
563 if (!ParseVarResult) {
564 logAllUnhandledErrors(ParseVarResult.takeError(), errs());
565 return true;
566 }
567 StringRef Name = ParseVarResult->Name;
568 bool IsPseudo = ParseVarResult->IsPseudo;
569
570 IsDefinition = (VarEndIdx != StringRef::npos);
571 SubstNeeded = !IsDefinition;
572 if (IsDefinition) {
573 if ((IsPseudo || !MatchStr.consume_front(":"))) {
574 SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
575 SourceMgr::DK_Error,
576 "invalid name in string variable definition");
577 return true;
578 }
579
580 // Detect collisions between string and numeric variables when the
581 // former is created later than the latter.
582 if (Context->GlobalNumericVariableTable.find(Name) !=
583 Context->GlobalNumericVariableTable.end()) {
584 SM.PrintMessage(
585 SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
586 "numeric variable with name '" + Name + "' already exists");
587 return true;
588 }
589 DefName = Name;
590 MatchRegexp = MatchStr;
591 } else {
592 if (IsPseudo) {
593 MatchStr = OrigMatchStr;
594 IsLegacyLineExpr = IsNumBlock = true;
595 } else
596 SubstStr = Name;
597 }
598 }
599
600 // Parse numeric substitution block.
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000601 std::unique_ptr<Expression> ExpressionPointer;
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000602 Optional<NumericVariable *> DefinedNumericVariable;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000603 if (IsNumBlock) {
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000604 Expected<std::unique_ptr<Expression>> ParseResult =
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000605 parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
606 IsLegacyLineExpr, LineNumber, Context,
607 SM);
608 if (!ParseResult) {
609 logAllUnhandledErrors(ParseResult.takeError(), errs());
610 return true;
611 }
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000612 ExpressionPointer = std::move(*ParseResult);
613 SubstNeeded = ExpressionPointer->getAST() != nullptr;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000614 if (DefinedNumericVariable) {
615 IsDefinition = true;
616 DefName = (*DefinedNumericVariable)->getName();
617 }
618 if (SubstNeeded)
619 SubstStr = MatchStr;
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000620 else {
621 ExpressionFormat Format = ExpressionPointer->getFormat();
622 MatchRegexp = cantFail(Format.getWildcardRegex());
623 }
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000624 }
625
626 // Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].
627 if (IsDefinition) {
628 RegExStr += '(';
629 ++SubstInsertIdx;
630
631 if (IsNumBlock) {
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000632 NumericVariableMatch NumericVariableDefinition = {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000633 *DefinedNumericVariable, CurParen};
634 NumericVariableDefs[DefName] = NumericVariableDefinition;
635 // This store is done here rather than in match() to allow
636 // parseNumericVariableUse() to get the pointer to the class instance
637 // of the right variable definition corresponding to a given numeric
638 // variable use.
639 Context->GlobalNumericVariableTable[DefName] =
640 *DefinedNumericVariable;
641 } else {
642 VariableDefs[DefName] = CurParen;
643 // Mark string variable as defined to detect collisions between
644 // string and numeric variables in parseNumericVariableUse() and
645 // defineCmdlineVariables() when the latter is created later than the
646 // former. We cannot reuse GlobalVariableTable for this by populating
647 // it with an empty string since we would then lose the ability to
648 // detect the use of an undefined variable in match().
649 Context->DefinedVariableTable[DefName] = true;
650 }
651
652 ++CurParen;
653 }
654
655 if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM))
656 return true;
657
658 if (IsDefinition)
659 RegExStr += ')';
660
661 // Handle substitutions: [[foo]] and [[#<foo expr>]].
662 if (SubstNeeded) {
663 // Handle substitution of string variables that were defined earlier on
664 // the same line by emitting a backreference. Expressions do not
665 // support substituting a numeric variable defined on the same line.
666 if (!IsNumBlock && VariableDefs.find(SubstStr) != VariableDefs.end()) {
667 unsigned CaptureParenGroup = VariableDefs[SubstStr];
668 if (CaptureParenGroup < 1 || CaptureParenGroup > 9) {
669 SM.PrintMessage(SMLoc::getFromPointer(SubstStr.data()),
670 SourceMgr::DK_Error,
671 "Can't back-reference more than 9 variables");
672 return true;
673 }
674 AddBackrefToRegEx(CaptureParenGroup);
675 } else {
676 // Handle substitution of string variables ([[<var>]]) defined in
677 // previous CHECK patterns, and substitution of expressions.
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000678 Substitution *Substitution =
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000679 IsNumBlock
680 ? Context->makeNumericSubstitution(
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000681 SubstStr, std::move(ExpressionPointer), SubstInsertIdx)
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000682 : Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
683 Substitutions.push_back(Substitution);
684 }
685 }
686 }
687
688 // Handle fixed string matches.
689 // Find the end, which is the start of the next regex.
690 size_t FixedMatchEnd = PatternStr.find("{{");
691 FixedMatchEnd = std::min(FixedMatchEnd, PatternStr.find("[["));
692 RegExStr += Regex::escape(PatternStr.substr(0, FixedMatchEnd));
693 PatternStr = PatternStr.substr(FixedMatchEnd);
694 }
695
696 if (MatchFullLinesHere) {
697 if (!Req.NoCanonicalizeWhiteSpace)
698 RegExStr += " *";
699 RegExStr += '$';
700 }
701
702 return false;
703}
704
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000705bool Pattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000706 Regex R(RS);
707 std::string Error;
708 if (!R.isValid(Error)) {
709 SM.PrintMessage(SMLoc::getFromPointer(RS.data()), SourceMgr::DK_Error,
710 "invalid regex: " + Error);
711 return true;
712 }
713
714 RegExStr += RS.str();
715 CurParen += R.getNumMatches();
716 return false;
717}
718
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000719void Pattern::AddBackrefToRegEx(unsigned BackrefNum) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000720 assert(BackrefNum >= 1 && BackrefNum <= 9 && "Invalid backref number");
721 std::string Backref = std::string("\\") + std::string(1, '0' + BackrefNum);
722 RegExStr += Backref;
723}
724
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000725Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
726 const SourceMgr &SM) const {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000727 // If this is the EOF pattern, match it immediately.
728 if (CheckTy == Check::CheckEOF) {
729 MatchLen = 0;
730 return Buffer.size();
731 }
732
733 // If this is a fixed string pattern, just match it now.
734 if (!FixedStr.empty()) {
735 MatchLen = FixedStr.size();
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000736 size_t Pos =
737 IgnoreCase ? Buffer.find_lower(FixedStr) : Buffer.find(FixedStr);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000738 if (Pos == StringRef::npos)
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000739 return make_error<NotFoundError>();
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000740 return Pos;
741 }
742
743 // Regex match.
744
745 // If there are substitutions, we need to create a temporary string with the
746 // actual value.
747 StringRef RegExToMatch = RegExStr;
748 std::string TmpStr;
749 if (!Substitutions.empty()) {
750 TmpStr = RegExStr;
751 if (LineNumber)
752 Context->LineVariable->setValue(*LineNumber);
753
754 size_t InsertOffset = 0;
755 // Substitute all string variables and expressions whose values are only
756 // now known. Use of string variables defined on the same line are handled
757 // by back-references.
758 for (const auto &Substitution : Substitutions) {
759 // Substitute and check for failure (e.g. use of undefined variable).
760 Expected<std::string> Value = Substitution->getResult();
761 if (!Value)
762 return Value.takeError();
763
764 // Plop it into the regex at the adjusted offset.
765 TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset,
766 Value->begin(), Value->end());
767 InsertOffset += Value->size();
768 }
769
770 // Match the newly constructed regex.
771 RegExToMatch = TmpStr;
772 }
773
774 SmallVector<StringRef, 4> MatchInfo;
Kai Nacke5b5b2fd2019-10-11 11:59:14 +0000775 unsigned int Flags = Regex::Newline;
776 if (IgnoreCase)
777 Flags |= Regex::IgnoreCase;
778 if (!Regex(RegExToMatch, Flags).match(Buffer, &MatchInfo))
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000779 return make_error<NotFoundError>();
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000780
781 // Successful regex match.
782 assert(!MatchInfo.empty() && "Didn't get any match");
783 StringRef FullMatch = MatchInfo[0];
784
785 // If this defines any string variables, remember their values.
786 for (const auto &VariableDef : VariableDefs) {
787 assert(VariableDef.second < MatchInfo.size() && "Internal paren error");
788 Context->GlobalVariableTable[VariableDef.first] =
789 MatchInfo[VariableDef.second];
790 }
791
792 // If this defines any numeric variables, remember their values.
793 for (const auto &NumericVariableDef : NumericVariableDefs) {
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000794 const NumericVariableMatch &NumericVariableMatch =
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000795 NumericVariableDef.getValue();
796 unsigned CaptureParenGroup = NumericVariableMatch.CaptureParenGroup;
797 assert(CaptureParenGroup < MatchInfo.size() && "Internal paren error");
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000798 NumericVariable *DefinedNumericVariable =
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000799 NumericVariableMatch.DefinedNumericVariable;
800
801 StringRef MatchedValue = MatchInfo[CaptureParenGroup];
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000802 ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat();
803 Expected<uint64_t> Value = Format.valueFromStringRepr(MatchedValue, SM);
804 if (!Value)
805 return Value.takeError();
806 DefinedNumericVariable->setValue(*Value);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000807 }
808
809 // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
810 // the required preceding newline, which is consumed by the pattern in the
811 // case of CHECK-EMPTY but not CHECK-NEXT.
812 size_t MatchStartSkip = CheckTy == Check::CheckEmpty;
813 MatchLen = FullMatch.size() - MatchStartSkip;
814 return FullMatch.data() - Buffer.data() + MatchStartSkip;
815}
816
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000817unsigned Pattern::computeMatchDistance(StringRef Buffer) const {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000818 // Just compute the number of matching characters. For regular expressions, we
819 // just compare against the regex itself and hope for the best.
820 //
821 // FIXME: One easy improvement here is have the regex lib generate a single
822 // example regular expression which matches, and use that as the example
823 // string.
824 StringRef ExampleString(FixedStr);
825 if (ExampleString.empty())
826 ExampleString = RegExStr;
827
828 // Only compare up to the first line in the buffer, or the string size.
829 StringRef BufferPrefix = Buffer.substr(0, ExampleString.size());
830 BufferPrefix = BufferPrefix.split('\n').first;
831 return BufferPrefix.edit_distance(ExampleString);
832}
833
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000834void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
835 SMRange MatchRange) const {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000836 // Print what we know about substitutions.
837 if (!Substitutions.empty()) {
838 for (const auto &Substitution : Substitutions) {
839 SmallString<256> Msg;
840 raw_svector_ostream OS(Msg);
841 Expected<std::string> MatchedValue = Substitution->getResult();
842
843 // Substitution failed or is not known at match time, print the undefined
844 // variables it uses.
845 if (!MatchedValue) {
846 bool UndefSeen = false;
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000847 handleAllErrors(MatchedValue.takeError(), [](const NotFoundError &E) {},
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000848 // Handled in PrintNoMatch().
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000849 [](const ErrorDiagnostic &E) {},
850 [&](const UndefVarError &E) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000851 if (!UndefSeen) {
852 OS << "uses undefined variable(s):";
853 UndefSeen = true;
854 }
855 OS << " ";
856 E.log(OS);
857 });
858 } else {
859 // Substitution succeeded. Print substituted value.
860 OS << "with \"";
861 OS.write_escaped(Substitution->getFromString()) << "\" equal to \"";
862 OS.write_escaped(*MatchedValue) << "\"";
863 }
864
865 if (MatchRange.isValid())
866 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, OS.str(),
867 {MatchRange});
868 else
869 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()),
870 SourceMgr::DK_Note, OS.str());
871 }
872 }
873}
874
875static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,
876 const SourceMgr &SM, SMLoc Loc,
877 Check::FileCheckType CheckTy,
878 StringRef Buffer, size_t Pos, size_t Len,
879 std::vector<FileCheckDiag> *Diags,
880 bool AdjustPrevDiag = false) {
881 SMLoc Start = SMLoc::getFromPointer(Buffer.data() + Pos);
882 SMLoc End = SMLoc::getFromPointer(Buffer.data() + Pos + Len);
883 SMRange Range(Start, End);
884 if (Diags) {
885 if (AdjustPrevDiag)
886 Diags->rbegin()->MatchTy = MatchTy;
887 else
888 Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range);
889 }
890 return Range;
891}
892
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000893void Pattern::printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
894 std::vector<FileCheckDiag> *Diags) const {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000895 // Attempt to find the closest/best fuzzy match. Usually an error happens
896 // because some string in the output didn't exactly match. In these cases, we
897 // would like to show the user a best guess at what "should have" matched, to
898 // save them having to actually check the input manually.
899 size_t NumLinesForward = 0;
900 size_t Best = StringRef::npos;
901 double BestQuality = 0;
902
903 // Use an arbitrary 4k limit on how far we will search.
904 for (size_t i = 0, e = std::min(size_t(4096), Buffer.size()); i != e; ++i) {
905 if (Buffer[i] == '\n')
906 ++NumLinesForward;
907
908 // Patterns have leading whitespace stripped, so skip whitespace when
909 // looking for something which looks like a pattern.
910 if (Buffer[i] == ' ' || Buffer[i] == '\t')
911 continue;
912
913 // Compute the "quality" of this match as an arbitrary combination of the
914 // match distance and the number of lines skipped to get to this match.
915 unsigned Distance = computeMatchDistance(Buffer.substr(i));
916 double Quality = Distance + (NumLinesForward / 100.);
917
918 if (Quality < BestQuality || Best == StringRef::npos) {
919 Best = i;
920 BestQuality = Quality;
921 }
922 }
923
924 // Print the "possible intended match here" line if we found something
925 // reasonable and not equal to what we showed in the "scanning from here"
926 // line.
927 if (Best && Best != StringRef::npos && BestQuality < 50) {
928 SMRange MatchRange =
929 ProcessMatchResult(FileCheckDiag::MatchFuzzy, SM, getLoc(),
930 getCheckTy(), Buffer, Best, 0, Diags);
931 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note,
932 "possible intended match here");
933
934 // FIXME: If we wanted to be really friendly we would show why the match
935 // failed, as it can be hard to spot simple one character differences.
936 }
937}
938
939Expected<StringRef>
940FileCheckPatternContext::getPatternVarValue(StringRef VarName) {
941 auto VarIter = GlobalVariableTable.find(VarName);
942 if (VarIter == GlobalVariableTable.end())
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000943 return make_error<UndefVarError>(VarName);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000944
945 return VarIter->second;
946}
947
948template <class... Types>
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000949NumericVariable *FileCheckPatternContext::makeNumericVariable(Types... args) {
950 NumericVariables.push_back(std::make_unique<NumericVariable>(args...));
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000951 return NumericVariables.back().get();
952}
953
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000954Substitution *
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000955FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
956 size_t InsertIdx) {
957 Substitutions.push_back(
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000958 std::make_unique<StringSubstitution>(this, VarName, InsertIdx));
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000959 return Substitutions.back().get();
960}
961
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000962Substitution *FileCheckPatternContext::makeNumericSubstitution(
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000963 StringRef ExpressionStr, std::unique_ptr<Expression> Expression,
964 size_t InsertIdx) {
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000965 Substitutions.push_back(std::make_unique<NumericSubstitution>(
Thomas Preud'homme8e966972019-03-05 23:20:29 +0000966 this, ExpressionStr, std::move(Expression), InsertIdx));
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000967 return Substitutions.back().get();
968}
969
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +0000970size_t Pattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +0000971 // Offset keeps track of the current offset within the input Str
972 size_t Offset = 0;
973 // [...] Nesting depth
974 size_t BracketDepth = 0;
975
976 while (!Str.empty()) {
977 if (Str.startswith("]]") && BracketDepth == 0)
978 return Offset;
979 if (Str[0] == '\\') {
980 // Backslash escapes the next char within regexes, so skip them both.
981 Str = Str.substr(2);
982 Offset += 2;
983 } else {
984 switch (Str[0]) {
985 default:
986 break;
987 case '[':
988 BracketDepth++;
989 break;
990 case ']':
991 if (BracketDepth == 0) {
992 SM.PrintMessage(SMLoc::getFromPointer(Str.data()),
993 SourceMgr::DK_Error,
994 "missing closing \"]\" for regex variable");
995 exit(1);
996 }
997 BracketDepth--;
998 break;
999 }
1000 Str = Str.substr(1);
1001 Offset++;
1002 }
1003 }
1004
1005 return StringRef::npos;
1006}
1007
1008StringRef FileCheck::CanonicalizeFile(MemoryBuffer &MB,
1009 SmallVectorImpl<char> &OutputBuffer) {
1010 OutputBuffer.reserve(MB.getBufferSize());
1011
1012 for (const char *Ptr = MB.getBufferStart(), *End = MB.getBufferEnd();
1013 Ptr != End; ++Ptr) {
1014 // Eliminate trailing dosish \r.
1015 if (Ptr <= End - 2 && Ptr[0] == '\r' && Ptr[1] == '\n') {
1016 continue;
1017 }
1018
1019 // If current char is not a horizontal whitespace or if horizontal
1020 // whitespace canonicalization is disabled, dump it to output as is.
1021 if (Req.NoCanonicalizeWhiteSpace || (*Ptr != ' ' && *Ptr != '\t')) {
1022 OutputBuffer.push_back(*Ptr);
1023 continue;
1024 }
1025
1026 // Otherwise, add one space and advance over neighboring space.
1027 OutputBuffer.push_back(' ');
1028 while (Ptr + 1 != End && (Ptr[1] == ' ' || Ptr[1] == '\t'))
1029 ++Ptr;
1030 }
1031
1032 // Add a null byte and then return all but that byte.
1033 OutputBuffer.push_back('\0');
1034 return StringRef(OutputBuffer.data(), OutputBuffer.size() - 1);
1035}
1036
1037FileCheckDiag::FileCheckDiag(const SourceMgr &SM,
1038 const Check::FileCheckType &CheckTy,
1039 SMLoc CheckLoc, MatchType MatchTy,
1040 SMRange InputRange)
1041 : CheckTy(CheckTy), MatchTy(MatchTy) {
1042 auto Start = SM.getLineAndColumn(InputRange.Start);
1043 auto End = SM.getLineAndColumn(InputRange.End);
1044 InputStartLine = Start.first;
1045 InputStartCol = Start.second;
1046 InputEndLine = End.first;
1047 InputEndCol = End.second;
1048 Start = SM.getLineAndColumn(CheckLoc);
1049 CheckLine = Start.first;
1050 CheckCol = Start.second;
1051}
1052
1053static bool IsPartOfWord(char c) {
1054 return (isalnum(c) || c == '-' || c == '_');
1055}
1056
1057Check::FileCheckType &Check::FileCheckType::setCount(int C) {
1058 assert(Count > 0 && "zero and negative counts are not supported");
1059 assert((C == 1 || Kind == CheckPlain) &&
1060 "count supported only for plain CHECK directives");
1061 Count = C;
1062 return *this;
1063}
1064
1065std::string Check::FileCheckType::getDescription(StringRef Prefix) const {
1066 switch (Kind) {
1067 case Check::CheckNone:
1068 return "invalid";
1069 case Check::CheckPlain:
1070 if (Count > 1)
1071 return Prefix.str() + "-COUNT";
Benjamin Krameradcd0262020-01-28 20:23:46 +01001072 return std::string(Prefix);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001073 case Check::CheckNext:
1074 return Prefix.str() + "-NEXT";
1075 case Check::CheckSame:
1076 return Prefix.str() + "-SAME";
1077 case Check::CheckNot:
1078 return Prefix.str() + "-NOT";
1079 case Check::CheckDAG:
1080 return Prefix.str() + "-DAG";
1081 case Check::CheckLabel:
1082 return Prefix.str() + "-LABEL";
1083 case Check::CheckEmpty:
1084 return Prefix.str() + "-EMPTY";
1085 case Check::CheckEOF:
1086 return "implicit EOF";
1087 case Check::CheckBadNot:
1088 return "bad NOT";
1089 case Check::CheckBadCount:
1090 return "bad COUNT";
1091 }
1092 llvm_unreachable("unknown FileCheckType");
1093}
1094
1095static std::pair<Check::FileCheckType, StringRef>
1096FindCheckType(StringRef Buffer, StringRef Prefix) {
1097 if (Buffer.size() <= Prefix.size())
1098 return {Check::CheckNone, StringRef()};
1099
1100 char NextChar = Buffer[Prefix.size()];
1101
1102 StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
1103 // Verify that the : is present after the prefix.
1104 if (NextChar == ':')
1105 return {Check::CheckPlain, Rest};
1106
1107 if (NextChar != '-')
1108 return {Check::CheckNone, StringRef()};
1109
1110 if (Rest.consume_front("COUNT-")) {
1111 int64_t Count;
1112 if (Rest.consumeInteger(10, Count))
1113 // Error happened in parsing integer.
1114 return {Check::CheckBadCount, Rest};
1115 if (Count <= 0 || Count > INT32_MAX)
1116 return {Check::CheckBadCount, Rest};
1117 if (!Rest.consume_front(":"))
1118 return {Check::CheckBadCount, Rest};
1119 return {Check::FileCheckType(Check::CheckPlain).setCount(Count), Rest};
1120 }
1121
1122 if (Rest.consume_front("NEXT:"))
1123 return {Check::CheckNext, Rest};
1124
1125 if (Rest.consume_front("SAME:"))
1126 return {Check::CheckSame, Rest};
1127
1128 if (Rest.consume_front("NOT:"))
1129 return {Check::CheckNot, Rest};
1130
1131 if (Rest.consume_front("DAG:"))
1132 return {Check::CheckDAG, Rest};
1133
1134 if (Rest.consume_front("LABEL:"))
1135 return {Check::CheckLabel, Rest};
1136
1137 if (Rest.consume_front("EMPTY:"))
1138 return {Check::CheckEmpty, Rest};
1139
1140 // You can't combine -NOT with another suffix.
1141 if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") ||
1142 Rest.startswith("NEXT-NOT:") || Rest.startswith("NOT-NEXT:") ||
1143 Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:") ||
1144 Rest.startswith("EMPTY-NOT:") || Rest.startswith("NOT-EMPTY:"))
1145 return {Check::CheckBadNot, Rest};
1146
1147 return {Check::CheckNone, Rest};
1148}
1149
1150// From the given position, find the next character after the word.
1151static size_t SkipWord(StringRef Str, size_t Loc) {
1152 while (Loc < Str.size() && IsPartOfWord(Str[Loc]))
1153 ++Loc;
1154 return Loc;
1155}
1156
1157/// Searches the buffer for the first prefix in the prefix regular expression.
1158///
1159/// This searches the buffer using the provided regular expression, however it
1160/// enforces constraints beyond that:
1161/// 1) The found prefix must not be a suffix of something that looks like
1162/// a valid prefix.
1163/// 2) The found prefix must be followed by a valid check type suffix using \c
1164/// FindCheckType above.
1165///
1166/// \returns a pair of StringRefs into the Buffer, which combines:
1167/// - the first match of the regular expression to satisfy these two is
1168/// returned,
1169/// otherwise an empty StringRef is returned to indicate failure.
1170/// - buffer rewound to the location right after parsed suffix, for parsing
1171/// to continue from
1172///
1173/// If this routine returns a valid prefix, it will also shrink \p Buffer to
1174/// start at the beginning of the returned prefix, increment \p LineNumber for
1175/// each new line consumed from \p Buffer, and set \p CheckTy to the type of
1176/// check found by examining the suffix.
1177///
1178/// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy
1179/// is unspecified.
1180static std::pair<StringRef, StringRef>
1181FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
1182 unsigned &LineNumber, Check::FileCheckType &CheckTy) {
1183 SmallVector<StringRef, 2> Matches;
1184
1185 while (!Buffer.empty()) {
1186 // Find the first (longest) match using the RE.
1187 if (!PrefixRE.match(Buffer, &Matches))
1188 // No match at all, bail.
1189 return {StringRef(), StringRef()};
1190
1191 StringRef Prefix = Matches[0];
1192 Matches.clear();
1193
1194 assert(Prefix.data() >= Buffer.data() &&
1195 Prefix.data() < Buffer.data() + Buffer.size() &&
1196 "Prefix doesn't start inside of buffer!");
1197 size_t Loc = Prefix.data() - Buffer.data();
1198 StringRef Skipped = Buffer.substr(0, Loc);
1199 Buffer = Buffer.drop_front(Loc);
1200 LineNumber += Skipped.count('\n');
1201
1202 // Check that the matched prefix isn't a suffix of some other check-like
1203 // word.
1204 // FIXME: This is a very ad-hoc check. it would be better handled in some
1205 // other way. Among other things it seems hard to distinguish between
1206 // intentional and unintentional uses of this feature.
1207 if (Skipped.empty() || !IsPartOfWord(Skipped.back())) {
1208 // Now extract the type.
1209 StringRef AfterSuffix;
1210 std::tie(CheckTy, AfterSuffix) = FindCheckType(Buffer, Prefix);
1211
1212 // If we've found a valid check type for this prefix, we're done.
1213 if (CheckTy != Check::CheckNone)
1214 return {Prefix, AfterSuffix};
1215 }
1216
1217 // If we didn't successfully find a prefix, we need to skip this invalid
1218 // prefix and continue scanning. We directly skip the prefix that was
1219 // matched and any additional parts of that check-like word.
1220 Buffer = Buffer.drop_front(SkipWord(Buffer, Prefix.size()));
1221 }
1222
1223 // We ran out of buffer while skipping partial matches so give up.
1224 return {StringRef(), StringRef()};
1225}
1226
1227void FileCheckPatternContext::createLineVariable() {
1228 assert(!LineVariable && "@LINE pseudo numeric variable already created");
1229 StringRef LineName = "@LINE";
Thomas Preud'homme8e966972019-03-05 23:20:29 +00001230 LineVariable = makeNumericVariable(
1231 LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001232 GlobalNumericVariableTable[LineName] = LineVariable;
1233}
1234
1235FileCheck::FileCheck(FileCheckRequest Req)
1236 : Req(Req), PatternContext(std::make_unique<FileCheckPatternContext>()),
1237 CheckStrings(std::make_unique<std::vector<FileCheckString>>()) {}
1238
1239FileCheck::~FileCheck() = default;
1240
1241bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,
1242 Regex &PrefixRE) {
1243 Error DefineError =
1244 PatternContext->defineCmdlineVariables(Req.GlobalDefines, SM);
1245 if (DefineError) {
1246 logAllUnhandledErrors(std::move(DefineError), errs());
1247 return true;
1248 }
1249
1250 PatternContext->createLineVariable();
1251
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001252 std::vector<Pattern> ImplicitNegativeChecks;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001253 for (const auto &PatternString : Req.ImplicitCheckNot) {
1254 // Create a buffer with fake command line content in order to display the
1255 // command line option responsible for the specific implicit CHECK-NOT.
1256 std::string Prefix = "-implicit-check-not='";
1257 std::string Suffix = "'";
1258 std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy(
1259 Prefix + PatternString + Suffix, "command line");
1260
1261 StringRef PatternInBuffer =
1262 CmdLine->getBuffer().substr(Prefix.size(), PatternString.size());
1263 SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
1264
1265 ImplicitNegativeChecks.push_back(
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001266 Pattern(Check::CheckNot, PatternContext.get()));
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001267 ImplicitNegativeChecks.back().parsePattern(PatternInBuffer,
1268 "IMPLICIT-CHECK", SM, Req);
1269 }
1270
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001271 std::vector<Pattern> DagNotMatches = ImplicitNegativeChecks;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001272
1273 // LineNumber keeps track of the line on which CheckPrefix instances are
1274 // found.
1275 unsigned LineNumber = 1;
1276
1277 while (1) {
1278 Check::FileCheckType CheckTy;
1279
1280 // See if a prefix occurs in the memory buffer.
1281 StringRef UsedPrefix;
1282 StringRef AfterSuffix;
1283 std::tie(UsedPrefix, AfterSuffix) =
1284 FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber, CheckTy);
1285 if (UsedPrefix.empty())
1286 break;
1287 assert(UsedPrefix.data() == Buffer.data() &&
1288 "Failed to move Buffer's start forward, or pointed prefix outside "
1289 "of the buffer!");
1290 assert(AfterSuffix.data() >= Buffer.data() &&
1291 AfterSuffix.data() < Buffer.data() + Buffer.size() &&
1292 "Parsing after suffix doesn't start inside of buffer!");
1293
1294 // Location to use for error messages.
1295 const char *UsedPrefixStart = UsedPrefix.data();
1296
1297 // Skip the buffer to the end of parsed suffix (or just prefix, if no good
1298 // suffix was processed).
1299 Buffer = AfterSuffix.empty() ? Buffer.drop_front(UsedPrefix.size())
1300 : AfterSuffix;
1301
1302 // Complain about useful-looking but unsupported suffixes.
1303 if (CheckTy == Check::CheckBadNot) {
1304 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,
1305 "unsupported -NOT combo on prefix '" + UsedPrefix + "'");
1306 return true;
1307 }
1308
1309 // Complain about invalid count specification.
1310 if (CheckTy == Check::CheckBadCount) {
1311 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,
1312 "invalid count in -COUNT specification on prefix '" +
1313 UsedPrefix + "'");
1314 return true;
1315 }
1316
1317 // Okay, we found the prefix, yay. Remember the rest of the line, but ignore
1318 // leading whitespace.
1319 if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
1320 Buffer = Buffer.substr(Buffer.find_first_not_of(" \t"));
1321
1322 // Scan ahead to the end of line.
1323 size_t EOL = Buffer.find_first_of("\n\r");
1324
1325 // Remember the location of the start of the pattern, for diagnostics.
1326 SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
1327
1328 // Parse the pattern.
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001329 Pattern P(CheckTy, PatternContext.get(), LineNumber);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001330 if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req))
1331 return true;
1332
1333 // Verify that CHECK-LABEL lines do not define or use variables
1334 if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {
1335 SM.PrintMessage(
1336 SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error,
1337 "found '" + UsedPrefix + "-LABEL:'"
1338 " with variable definition or use");
1339 return true;
1340 }
1341
1342 Buffer = Buffer.substr(EOL);
1343
1344 // Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them.
1345 if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame ||
1346 CheckTy == Check::CheckEmpty) &&
1347 CheckStrings->empty()) {
1348 StringRef Type = CheckTy == Check::CheckNext
1349 ? "NEXT"
1350 : CheckTy == Check::CheckEmpty ? "EMPTY" : "SAME";
1351 SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
1352 SourceMgr::DK_Error,
1353 "found '" + UsedPrefix + "-" + Type +
1354 "' without previous '" + UsedPrefix + ": line");
1355 return true;
1356 }
1357
1358 // Handle CHECK-DAG/-NOT.
1359 if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) {
1360 DagNotMatches.push_back(P);
1361 continue;
1362 }
1363
1364 // Okay, add the string we captured to the output vector and move on.
1365 CheckStrings->emplace_back(P, UsedPrefix, PatternLoc);
1366 std::swap(DagNotMatches, CheckStrings->back().DagNotStrings);
1367 DagNotMatches = ImplicitNegativeChecks;
1368 }
1369
1370 // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first
1371 // prefix as a filler for the error message.
1372 if (!DagNotMatches.empty()) {
1373 CheckStrings->emplace_back(
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001374 Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1),
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001375 *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
1376 std::swap(DagNotMatches, CheckStrings->back().DagNotStrings);
1377 }
1378
1379 if (CheckStrings->empty()) {
1380 errs() << "error: no check strings found with prefix"
1381 << (Req.CheckPrefixes.size() > 1 ? "es " : " ");
1382 auto I = Req.CheckPrefixes.begin();
1383 auto E = Req.CheckPrefixes.end();
1384 if (I != E) {
1385 errs() << "\'" << *I << ":'";
1386 ++I;
1387 }
1388 for (; I != E; ++I)
1389 errs() << ", \'" << *I << ":'";
1390
1391 errs() << '\n';
1392 return true;
1393 }
1394
1395 return false;
1396}
1397
1398static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001399 StringRef Prefix, SMLoc Loc, const Pattern &Pat,
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001400 int MatchedCount, StringRef Buffer, size_t MatchPos,
1401 size_t MatchLen, const FileCheckRequest &Req,
1402 std::vector<FileCheckDiag> *Diags) {
1403 bool PrintDiag = true;
1404 if (ExpectedMatch) {
1405 if (!Req.Verbose)
1406 return;
1407 if (!Req.VerboseVerbose && Pat.getCheckTy() == Check::CheckEOF)
1408 return;
1409 // Due to their verbosity, we don't print verbose diagnostics here if we're
1410 // gathering them for a different rendering, but we always print other
1411 // diagnostics.
1412 PrintDiag = !Diags;
1413 }
1414 SMRange MatchRange = ProcessMatchResult(
1415 ExpectedMatch ? FileCheckDiag::MatchFoundAndExpected
1416 : FileCheckDiag::MatchFoundButExcluded,
1417 SM, Loc, Pat.getCheckTy(), Buffer, MatchPos, MatchLen, Diags);
1418 if (!PrintDiag)
1419 return;
1420
1421 std::string Message = formatv("{0}: {1} string found in input",
1422 Pat.getCheckTy().getDescription(Prefix),
1423 (ExpectedMatch ? "expected" : "excluded"))
1424 .str();
1425 if (Pat.getCount() > 1)
1426 Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
1427
1428 SM.PrintMessage(
1429 Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
1430 SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
1431 {MatchRange});
1432 Pat.printSubstitutions(SM, Buffer, MatchRange);
1433}
1434
1435static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM,
1436 const FileCheckString &CheckStr, int MatchedCount,
1437 StringRef Buffer, size_t MatchPos, size_t MatchLen,
1438 FileCheckRequest &Req,
1439 std::vector<FileCheckDiag> *Diags) {
1440 PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
1441 MatchedCount, Buffer, MatchPos, MatchLen, Req, Diags);
1442}
1443
1444static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001445 StringRef Prefix, SMLoc Loc, const Pattern &Pat,
1446 int MatchedCount, StringRef Buffer,
1447 bool VerboseVerbose, std::vector<FileCheckDiag> *Diags,
1448 Error MatchErrors) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001449 assert(MatchErrors && "Called on successful match");
1450 bool PrintDiag = true;
1451 if (!ExpectedMatch) {
1452 if (!VerboseVerbose) {
1453 consumeError(std::move(MatchErrors));
1454 return;
1455 }
1456 // Due to their verbosity, we don't print verbose diagnostics here if we're
1457 // gathering them for a different rendering, but we always print other
1458 // diagnostics.
1459 PrintDiag = !Diags;
1460 }
1461
1462 // If the current position is at the end of a line, advance to the start of
1463 // the next line.
1464 Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r"));
1465 SMRange SearchRange = ProcessMatchResult(
1466 ExpectedMatch ? FileCheckDiag::MatchNoneButExpected
1467 : FileCheckDiag::MatchNoneAndExcluded,
1468 SM, Loc, Pat.getCheckTy(), Buffer, 0, Buffer.size(), Diags);
1469 if (!PrintDiag) {
1470 consumeError(std::move(MatchErrors));
1471 return;
1472 }
1473
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001474 MatchErrors = handleErrors(std::move(MatchErrors),
1475 [](const ErrorDiagnostic &E) { E.log(errs()); });
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001476
1477 // No problem matching the string per se.
1478 if (!MatchErrors)
1479 return;
1480 consumeError(std::move(MatchErrors));
1481
1482 // Print "not found" diagnostic.
1483 std::string Message = formatv("{0}: {1} string not found in input",
1484 Pat.getCheckTy().getDescription(Prefix),
1485 (ExpectedMatch ? "expected" : "excluded"))
1486 .str();
1487 if (Pat.getCount() > 1)
1488 Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
1489 SM.PrintMessage(
1490 Loc, ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark, Message);
1491
1492 // Print the "scanning from here" line.
1493 SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here");
1494
1495 // Allow the pattern to print additional information if desired.
1496 Pat.printSubstitutions(SM, Buffer);
1497
1498 if (ExpectedMatch)
1499 Pat.printFuzzyMatch(SM, Buffer, Diags);
1500}
1501
1502static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
1503 const FileCheckString &CheckStr, int MatchedCount,
1504 StringRef Buffer, bool VerboseVerbose,
1505 std::vector<FileCheckDiag> *Diags, Error MatchErrors) {
1506 PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
1507 MatchedCount, Buffer, VerboseVerbose, Diags,
1508 std::move(MatchErrors));
1509}
1510
1511/// Counts the number of newlines in the specified range.
1512static unsigned CountNumNewlinesBetween(StringRef Range,
1513 const char *&FirstNewLine) {
1514 unsigned NumNewLines = 0;
1515 while (1) {
1516 // Scan for newline.
1517 Range = Range.substr(Range.find_first_of("\n\r"));
1518 if (Range.empty())
1519 return NumNewLines;
1520
1521 ++NumNewLines;
1522
1523 // Handle \n\r and \r\n as a single newline.
1524 if (Range.size() > 1 && (Range[1] == '\n' || Range[1] == '\r') &&
1525 (Range[0] != Range[1]))
1526 Range = Range.substr(1);
1527 Range = Range.substr(1);
1528
1529 if (NumNewLines == 1)
1530 FirstNewLine = Range.begin();
1531 }
1532}
1533
1534size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
1535 bool IsLabelScanMode, size_t &MatchLen,
1536 FileCheckRequest &Req,
1537 std::vector<FileCheckDiag> *Diags) const {
1538 size_t LastPos = 0;
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001539 std::vector<const Pattern *> NotStrings;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001540
1541 // IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL
1542 // bounds; we have not processed variable definitions within the bounded block
1543 // yet so cannot handle any final CHECK-DAG yet; this is handled when going
1544 // over the block again (including the last CHECK-LABEL) in normal mode.
1545 if (!IsLabelScanMode) {
1546 // Match "dag strings" (with mixed "not strings" if any).
1547 LastPos = CheckDag(SM, Buffer, NotStrings, Req, Diags);
1548 if (LastPos == StringRef::npos)
1549 return StringRef::npos;
1550 }
1551
1552 // Match itself from the last position after matching CHECK-DAG.
1553 size_t LastMatchEnd = LastPos;
1554 size_t FirstMatchPos = 0;
1555 // Go match the pattern Count times. Majority of patterns only match with
1556 // count 1 though.
1557 assert(Pat.getCount() != 0 && "pattern count can not be zero");
1558 for (int i = 1; i <= Pat.getCount(); i++) {
1559 StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
1560 size_t CurrentMatchLen;
1561 // get a match at current start point
1562 Expected<size_t> MatchResult = Pat.match(MatchBuffer, CurrentMatchLen, SM);
1563
1564 // report
1565 if (!MatchResult) {
1566 PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags,
1567 MatchResult.takeError());
1568 return StringRef::npos;
1569 }
1570 size_t MatchPos = *MatchResult;
1571 PrintMatch(true, SM, *this, i, MatchBuffer, MatchPos, CurrentMatchLen, Req,
1572 Diags);
1573 if (i == 1)
1574 FirstMatchPos = LastPos + MatchPos;
1575
1576 // move start point after the match
1577 LastMatchEnd += MatchPos + CurrentMatchLen;
1578 }
1579 // Full match len counts from first match pos.
1580 MatchLen = LastMatchEnd - FirstMatchPos;
1581
1582 // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
1583 // or CHECK-NOT
1584 if (!IsLabelScanMode) {
1585 size_t MatchPos = FirstMatchPos - LastPos;
1586 StringRef MatchBuffer = Buffer.substr(LastPos);
1587 StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
1588
1589 // If this check is a "CHECK-NEXT", verify that the previous match was on
1590 // the previous line (i.e. that there is one newline between them).
1591 if (CheckNext(SM, SkippedRegion)) {
1592 ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,
1593 Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
1594 Diags, Req.Verbose);
1595 return StringRef::npos;
1596 }
1597
1598 // If this check is a "CHECK-SAME", verify that the previous match was on
1599 // the same line (i.e. that there is no newline between them).
1600 if (CheckSame(SM, SkippedRegion)) {
1601 ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,
1602 Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
1603 Diags, Req.Verbose);
1604 return StringRef::npos;
1605 }
1606
1607 // If this match had "not strings", verify that they don't exist in the
1608 // skipped region.
1609 if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
1610 return StringRef::npos;
1611 }
1612
1613 return FirstMatchPos;
1614}
1615
1616bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
1617 if (Pat.getCheckTy() != Check::CheckNext &&
1618 Pat.getCheckTy() != Check::CheckEmpty)
1619 return false;
1620
1621 Twine CheckName =
1622 Prefix +
1623 Twine(Pat.getCheckTy() == Check::CheckEmpty ? "-EMPTY" : "-NEXT");
1624
1625 // Count the number of newlines between the previous match and this one.
1626 const char *FirstNewLine = nullptr;
1627 unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
1628
1629 if (NumNewLines == 0) {
1630 SM.PrintMessage(Loc, SourceMgr::DK_Error,
1631 CheckName + ": is on the same line as previous match");
1632 SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
1633 "'next' match was here");
1634 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
1635 "previous match ended here");
1636 return true;
1637 }
1638
1639 if (NumNewLines != 1) {
1640 SM.PrintMessage(Loc, SourceMgr::DK_Error,
1641 CheckName +
1642 ": is not on the line after the previous match");
1643 SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
1644 "'next' match was here");
1645 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
1646 "previous match ended here");
1647 SM.PrintMessage(SMLoc::getFromPointer(FirstNewLine), SourceMgr::DK_Note,
1648 "non-matching line after previous match is here");
1649 return true;
1650 }
1651
1652 return false;
1653}
1654
1655bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const {
1656 if (Pat.getCheckTy() != Check::CheckSame)
1657 return false;
1658
1659 // Count the number of newlines between the previous match and this one.
1660 const char *FirstNewLine = nullptr;
1661 unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
1662
1663 if (NumNewLines != 0) {
1664 SM.PrintMessage(Loc, SourceMgr::DK_Error,
1665 Prefix +
1666 "-SAME: is not on the same line as the previous match");
1667 SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
1668 "'next' match was here");
1669 SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
1670 "previous match ended here");
1671 return true;
1672 }
1673
1674 return false;
1675}
1676
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001677bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
1678 const std::vector<const Pattern *> &NotStrings,
1679 const FileCheckRequest &Req,
1680 std::vector<FileCheckDiag> *Diags) const {
1681 for (const Pattern *Pat : NotStrings) {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001682 assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
1683
1684 size_t MatchLen = 0;
1685 Expected<size_t> MatchResult = Pat->match(Buffer, MatchLen, SM);
1686
1687 if (!MatchResult) {
1688 PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
1689 Req.VerboseVerbose, Diags, MatchResult.takeError());
1690 continue;
1691 }
1692 size_t Pos = *MatchResult;
1693
1694 PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen,
1695 Req, Diags);
1696
1697 return true;
1698 }
1699
1700 return false;
1701}
1702
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001703size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
1704 std::vector<const Pattern *> &NotStrings,
1705 const FileCheckRequest &Req,
1706 std::vector<FileCheckDiag> *Diags) const {
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001707 if (DagNotStrings.empty())
1708 return 0;
1709
1710 // The start of the search range.
1711 size_t StartPos = 0;
1712
1713 struct MatchRange {
1714 size_t Pos;
1715 size_t End;
1716 };
1717 // A sorted list of ranges for non-overlapping CHECK-DAG matches. Match
1718 // ranges are erased from this list once they are no longer in the search
1719 // range.
1720 std::list<MatchRange> MatchRanges;
1721
1722 // We need PatItr and PatEnd later for detecting the end of a CHECK-DAG
1723 // group, so we don't use a range-based for loop here.
1724 for (auto PatItr = DagNotStrings.begin(), PatEnd = DagNotStrings.end();
1725 PatItr != PatEnd; ++PatItr) {
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001726 const Pattern &Pat = *PatItr;
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001727 assert((Pat.getCheckTy() == Check::CheckDAG ||
1728 Pat.getCheckTy() == Check::CheckNot) &&
1729 "Invalid CHECK-DAG or CHECK-NOT!");
1730
1731 if (Pat.getCheckTy() == Check::CheckNot) {
1732 NotStrings.push_back(&Pat);
1733 continue;
1734 }
1735
1736 assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!");
1737
1738 // CHECK-DAG always matches from the start.
1739 size_t MatchLen = 0, MatchPos = StartPos;
1740
1741 // Search for a match that doesn't overlap a previous match in this
1742 // CHECK-DAG group.
1743 for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
1744 StringRef MatchBuffer = Buffer.substr(MatchPos);
1745 Expected<size_t> MatchResult = Pat.match(MatchBuffer, MatchLen, SM);
1746 // With a group of CHECK-DAGs, a single mismatching means the match on
1747 // that group of CHECK-DAGs fails immediately.
1748 if (!MatchResult) {
1749 PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer,
1750 Req.VerboseVerbose, Diags, MatchResult.takeError());
1751 return StringRef::npos;
1752 }
1753 size_t MatchPosBuf = *MatchResult;
1754 // Re-calc it as the offset relative to the start of the original string.
1755 MatchPos += MatchPosBuf;
1756 if (Req.VerboseVerbose)
1757 PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
1758 MatchLen, Req, Diags);
1759 MatchRange M{MatchPos, MatchPos + MatchLen};
1760 if (Req.AllowDeprecatedDagOverlap) {
1761 // We don't need to track all matches in this mode, so we just maintain
1762 // one match range that encompasses the current CHECK-DAG group's
1763 // matches.
1764 if (MatchRanges.empty())
1765 MatchRanges.insert(MatchRanges.end(), M);
1766 else {
1767 auto Block = MatchRanges.begin();
1768 Block->Pos = std::min(Block->Pos, M.Pos);
1769 Block->End = std::max(Block->End, M.End);
1770 }
1771 break;
1772 }
1773 // Iterate previous matches until overlapping match or insertion point.
1774 bool Overlap = false;
1775 for (; MI != ME; ++MI) {
1776 if (M.Pos < MI->End) {
1777 // !Overlap => New match has no overlap and is before this old match.
1778 // Overlap => New match overlaps this old match.
1779 Overlap = MI->Pos < M.End;
1780 break;
1781 }
1782 }
1783 if (!Overlap) {
1784 // Insert non-overlapping match into list.
1785 MatchRanges.insert(MI, M);
1786 break;
1787 }
1788 if (Req.VerboseVerbose) {
1789 // Due to their verbosity, we don't print verbose diagnostics here if
1790 // we're gathering them for a different rendering, but we always print
1791 // other diagnostics.
1792 if (!Diags) {
1793 SMLoc OldStart = SMLoc::getFromPointer(Buffer.data() + MI->Pos);
1794 SMLoc OldEnd = SMLoc::getFromPointer(Buffer.data() + MI->End);
1795 SMRange OldRange(OldStart, OldEnd);
1796 SM.PrintMessage(OldStart, SourceMgr::DK_Note,
1797 "match discarded, overlaps earlier DAG match here",
1798 {OldRange});
1799 } else
1800 Diags->rbegin()->MatchTy = FileCheckDiag::MatchFoundButDiscarded;
1801 }
1802 MatchPos = MI->End;
1803 }
1804 if (!Req.VerboseVerbose)
1805 PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos,
1806 MatchLen, Req, Diags);
1807
1808 // Handle the end of a CHECK-DAG group.
1809 if (std::next(PatItr) == PatEnd ||
1810 std::next(PatItr)->getCheckTy() == Check::CheckNot) {
1811 if (!NotStrings.empty()) {
1812 // If there are CHECK-NOTs between two CHECK-DAGs or from CHECK to
1813 // CHECK-DAG, verify that there are no 'not' strings occurred in that
1814 // region.
1815 StringRef SkippedRegion =
1816 Buffer.slice(StartPos, MatchRanges.begin()->Pos);
1817 if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
1818 return StringRef::npos;
1819 // Clear "not strings".
1820 NotStrings.clear();
1821 }
1822 // All subsequent CHECK-DAGs and CHECK-NOTs should be matched from the
1823 // end of this CHECK-DAG group's match range.
1824 StartPos = MatchRanges.rbegin()->End;
1825 // Don't waste time checking for (impossible) overlaps before that.
1826 MatchRanges.clear();
1827 }
1828 }
1829
1830 return StartPos;
1831}
1832
1833// A check prefix must contain only alphanumeric, hyphens and underscores.
1834static bool ValidateCheckPrefix(StringRef CheckPrefix) {
1835 static const Regex Validator("^[a-zA-Z0-9_-]*$");
1836 return Validator.match(CheckPrefix);
1837}
1838
1839bool FileCheck::ValidateCheckPrefixes() {
1840 StringSet<> PrefixSet;
1841
1842 for (StringRef Prefix : Req.CheckPrefixes) {
1843 // Reject empty prefixes.
1844 if (Prefix == "")
1845 return false;
1846
1847 if (!PrefixSet.insert(Prefix).second)
1848 return false;
1849
1850 if (!ValidateCheckPrefix(Prefix))
1851 return false;
1852 }
1853
1854 return true;
1855}
1856
1857Regex FileCheck::buildCheckPrefixRegex() {
1858 // I don't think there's a way to specify an initial value for cl::list,
1859 // so if nothing was specified, add the default
1860 if (Req.CheckPrefixes.empty())
1861 Req.CheckPrefixes.push_back("CHECK");
1862
1863 // We already validated the contents of CheckPrefixes so just concatenate
1864 // them as alternatives.
1865 SmallString<32> PrefixRegexStr;
1866 for (StringRef Prefix : Req.CheckPrefixes) {
1867 if (Prefix != Req.CheckPrefixes.front())
1868 PrefixRegexStr.push_back('|');
1869
1870 PrefixRegexStr.append(Prefix);
1871 }
1872
1873 return Regex(PrefixRegexStr);
1874}
1875
1876Error FileCheckPatternContext::defineCmdlineVariables(
1877 std::vector<std::string> &CmdlineDefines, SourceMgr &SM) {
1878 assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&
1879 "Overriding defined variable with command-line variable definitions");
1880
1881 if (CmdlineDefines.empty())
1882 return Error::success();
1883
1884 // Create a string representing the vector of command-line definitions. Each
1885 // definition is on its own line and prefixed with a definition number to
1886 // clarify which definition a given diagnostic corresponds to.
1887 unsigned I = 0;
1888 Error Errs = Error::success();
1889 std::string CmdlineDefsDiag;
1890 SmallVector<std::pair<size_t, size_t>, 4> CmdlineDefsIndices;
1891 for (StringRef CmdlineDef : CmdlineDefines) {
1892 std::string DefPrefix = ("Global define #" + Twine(++I) + ": ").str();
1893 size_t EqIdx = CmdlineDef.find('=');
1894 if (EqIdx == StringRef::npos) {
1895 CmdlineDefsIndices.push_back(std::make_pair(CmdlineDefsDiag.size(), 0));
1896 continue;
1897 }
1898 // Numeric variable definition.
1899 if (CmdlineDef[0] == '#') {
1900 // Append a copy of the command-line definition adapted to use the same
1901 // format as in the input file to be able to reuse
1902 // parseNumericSubstitutionBlock.
1903 CmdlineDefsDiag += (DefPrefix + CmdlineDef + " (parsed as: [[").str();
Benjamin Krameradcd0262020-01-28 20:23:46 +01001904 std::string SubstitutionStr = std::string(CmdlineDef);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001905 SubstitutionStr[EqIdx] = ':';
1906 CmdlineDefsIndices.push_back(
1907 std::make_pair(CmdlineDefsDiag.size(), SubstitutionStr.size()));
1908 CmdlineDefsDiag += (SubstitutionStr + Twine("]])\n")).str();
1909 } else {
1910 CmdlineDefsDiag += DefPrefix;
1911 CmdlineDefsIndices.push_back(
1912 std::make_pair(CmdlineDefsDiag.size(), CmdlineDef.size()));
1913 CmdlineDefsDiag += (CmdlineDef + "\n").str();
1914 }
1915 }
1916
1917 // Create a buffer with fake command line content in order to display
1918 // parsing diagnostic with location information and point to the
1919 // global definition with invalid syntax.
1920 std::unique_ptr<MemoryBuffer> CmdLineDefsDiagBuffer =
1921 MemoryBuffer::getMemBufferCopy(CmdlineDefsDiag, "Global defines");
1922 StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();
1923 SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc());
1924
1925 for (std::pair<size_t, size_t> CmdlineDefIndices : CmdlineDefsIndices) {
1926 StringRef CmdlineDef = CmdlineDefsDiagRef.substr(CmdlineDefIndices.first,
1927 CmdlineDefIndices.second);
1928 if (CmdlineDef.empty()) {
1929 Errs = joinErrors(
1930 std::move(Errs),
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001931 ErrorDiagnostic::get(SM, CmdlineDef,
1932 "missing equal sign in global definition"));
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001933 continue;
1934 }
1935
1936 // Numeric variable definition.
1937 if (CmdlineDef[0] == '#') {
1938 // Now parse the definition both to check that the syntax is correct and
1939 // to create the necessary class instance.
1940 StringRef CmdlineDefExpr = CmdlineDef.substr(1);
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001941 Optional<NumericVariable *> DefinedNumericVariable;
Thomas Preud'homme8e966972019-03-05 23:20:29 +00001942 Expected<std::unique_ptr<Expression>> ExpressionResult =
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001943 Pattern::parseNumericSubstitutionBlock(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001944 CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM);
Thomas Preud'homme8e966972019-03-05 23:20:29 +00001945 if (!ExpressionResult) {
1946 Errs = joinErrors(std::move(Errs), ExpressionResult.takeError());
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001947 continue;
1948 }
Thomas Preud'homme8e966972019-03-05 23:20:29 +00001949 std::unique_ptr<Expression> Expression = std::move(*ExpressionResult);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001950 // Now evaluate the expression whose value this variable should be set
1951 // to, since the expression of a command-line variable definition should
1952 // only use variables defined earlier on the command-line. If not, this
1953 // is an error and we report it.
Thomas Preud'homme8e966972019-03-05 23:20:29 +00001954 Expected<uint64_t> Value = Expression->getAST()->eval();
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001955 if (!Value) {
1956 Errs = joinErrors(std::move(Errs), Value.takeError());
1957 continue;
1958 }
1959
1960 assert(DefinedNumericVariable && "No variable defined");
1961 (*DefinedNumericVariable)->setValue(*Value);
1962
1963 // Record this variable definition.
1964 GlobalNumericVariableTable[(*DefinedNumericVariable)->getName()] =
1965 *DefinedNumericVariable;
1966 } else {
1967 // String variable definition.
1968 std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
1969 StringRef CmdlineName = CmdlineNameVal.first;
1970 StringRef OrigCmdlineName = CmdlineName;
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001971 Expected<Pattern::VariableProperties> ParseVarResult =
1972 Pattern::parseVariable(CmdlineName, SM);
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001973 if (!ParseVarResult) {
1974 Errs = joinErrors(std::move(Errs), ParseVarResult.takeError());
1975 continue;
1976 }
1977 // Check that CmdlineName does not denote a pseudo variable is only
1978 // composed of the parsed numeric variable. This catches cases like
1979 // "FOO+2" in a "FOO+2=10" definition.
1980 if (ParseVarResult->IsPseudo || !CmdlineName.empty()) {
1981 Errs = joinErrors(std::move(Errs),
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001982 ErrorDiagnostic::get(
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001983 SM, OrigCmdlineName,
1984 "invalid name in string variable definition '" +
1985 OrigCmdlineName + "'"));
1986 continue;
1987 }
1988 StringRef Name = ParseVarResult->Name;
1989
1990 // Detect collisions between string and numeric variables when the former
1991 // is created later than the latter.
1992 if (GlobalNumericVariableTable.find(Name) !=
1993 GlobalNumericVariableTable.end()) {
Thomas Preud'hommed8fd92e2019-12-11 23:48:01 +00001994 Errs = joinErrors(std::move(Errs),
1995 ErrorDiagnostic::get(SM, Name,
Dmitri Gribenkod3aed7f2019-10-10 14:27:14 +00001996 "numeric variable with name '" +
1997 Name + "' already exists"));
1998 continue;
1999 }
2000 GlobalVariableTable.insert(CmdlineNameVal);
2001 // Mark the string variable as defined to detect collisions between
2002 // string and numeric variables in defineCmdlineVariables when the latter
2003 // is created later than the former. We cannot reuse GlobalVariableTable
2004 // for this by populating it with an empty string since we would then
2005 // lose the ability to detect the use of an undefined variable in
2006 // match().
2007 DefinedVariableTable[Name] = true;
2008 }
2009 }
2010
2011 return Errs;
2012}
2013
2014void FileCheckPatternContext::clearLocalVars() {
2015 SmallVector<StringRef, 16> LocalPatternVars, LocalNumericVars;
2016 for (const StringMapEntry<StringRef> &Var : GlobalVariableTable)
2017 if (Var.first()[0] != '$')
2018 LocalPatternVars.push_back(Var.first());
2019
2020 // Numeric substitution reads the value of a variable directly, not via
2021 // GlobalNumericVariableTable. Therefore, we clear local variables by
2022 // clearing their value which will lead to a numeric substitution failure. We
2023 // also mark the variable for removal from GlobalNumericVariableTable since
2024 // this is what defineCmdlineVariables checks to decide that no global
2025 // variable has been defined.
2026 for (const auto &Var : GlobalNumericVariableTable)
2027 if (Var.first()[0] != '$') {
2028 Var.getValue()->clearValue();
2029 LocalNumericVars.push_back(Var.first());
2030 }
2031
2032 for (const auto &Var : LocalPatternVars)
2033 GlobalVariableTable.erase(Var);
2034 for (const auto &Var : LocalNumericVars)
2035 GlobalNumericVariableTable.erase(Var);
2036}
2037
2038bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
2039 std::vector<FileCheckDiag> *Diags) {
2040 bool ChecksFailed = false;
2041
2042 unsigned i = 0, j = 0, e = CheckStrings->size();
2043 while (true) {
2044 StringRef CheckRegion;
2045 if (j == e) {
2046 CheckRegion = Buffer;
2047 } else {
2048 const FileCheckString &CheckLabelStr = (*CheckStrings)[j];
2049 if (CheckLabelStr.Pat.getCheckTy() != Check::CheckLabel) {
2050 ++j;
2051 continue;
2052 }
2053
2054 // Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG
2055 size_t MatchLabelLen = 0;
2056 size_t MatchLabelPos =
2057 CheckLabelStr.Check(SM, Buffer, true, MatchLabelLen, Req, Diags);
2058 if (MatchLabelPos == StringRef::npos)
2059 // Immediately bail if CHECK-LABEL fails, nothing else we can do.
2060 return false;
2061
2062 CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen);
2063 Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen);
2064 ++j;
2065 }
2066
2067 // Do not clear the first region as it's the one before the first
2068 // CHECK-LABEL and it would clear variables defined on the command-line
2069 // before they get used.
2070 if (i != 0 && Req.EnableVarScope)
2071 PatternContext->clearLocalVars();
2072
2073 for (; i != j; ++i) {
2074 const FileCheckString &CheckStr = (*CheckStrings)[i];
2075
2076 // Check each string within the scanned region, including a second check
2077 // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)
2078 size_t MatchLen = 0;
2079 size_t MatchPos =
2080 CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);
2081
2082 if (MatchPos == StringRef::npos) {
2083 ChecksFailed = true;
2084 i = j;
2085 break;
2086 }
2087
2088 CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
2089 }
2090
2091 if (j == e)
2092 break;
2093 }
2094
2095 // Success if no checks failed.
2096 return !ChecksFailed;
2097}