1d8e91e46SDimitry Andric //===- FileCheck.cpp - Check that File's Contents match what is expected --===//
2d8e91e46SDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d8e91e46SDimitry Andric //
7d8e91e46SDimitry Andric //===----------------------------------------------------------------------===//
8d8e91e46SDimitry Andric //
9d8e91e46SDimitry Andric // FileCheck does a line-by line check of a file that validates whether it
10d8e91e46SDimitry Andric // contains the expected content. This is useful for regression tests etc.
11d8e91e46SDimitry Andric //
12d8e91e46SDimitry Andric // This file implements most of the API that will be used by the FileCheck utility
13d8e91e46SDimitry Andric // as well as various unittests.
14d8e91e46SDimitry Andric //===----------------------------------------------------------------------===//
15d8e91e46SDimitry Andric
16b60736ecSDimitry Andric #include "llvm/FileCheck/FileCheck.h"
171d5ae102SDimitry Andric #include "FileCheckImpl.h"
18b60736ecSDimitry Andric #include "llvm/ADT/STLExtras.h"
197fa27ce4SDimitry Andric #include "llvm/ADT/StringExtras.h"
20d8e91e46SDimitry Andric #include "llvm/ADT/StringSet.h"
211d5ae102SDimitry Andric #include "llvm/ADT/Twine.h"
22cfca06d7SDimitry Andric #include "llvm/Support/CheckedArithmetic.h"
23d8e91e46SDimitry Andric #include "llvm/Support/FormatVariadic.h"
24d8e91e46SDimitry Andric #include <cstdint>
25d8e91e46SDimitry Andric #include <list>
26b60736ecSDimitry Andric #include <set>
27d8e91e46SDimitry Andric #include <tuple>
28d8e91e46SDimitry Andric #include <utility>
29d8e91e46SDimitry Andric
30d8e91e46SDimitry Andric using namespace llvm;
31d8e91e46SDimitry Andric
toString() const32cfca06d7SDimitry Andric StringRef ExpressionFormat::toString() const {
33cfca06d7SDimitry Andric switch (Value) {
34cfca06d7SDimitry Andric case Kind::NoFormat:
35cfca06d7SDimitry Andric return StringRef("<none>");
36cfca06d7SDimitry Andric case Kind::Unsigned:
37cfca06d7SDimitry Andric return StringRef("%u");
38cfca06d7SDimitry Andric case Kind::Signed:
39cfca06d7SDimitry Andric return StringRef("%d");
40cfca06d7SDimitry Andric case Kind::HexUpper:
41cfca06d7SDimitry Andric return StringRef("%X");
42cfca06d7SDimitry Andric case Kind::HexLower:
43cfca06d7SDimitry Andric return StringRef("%x");
44cfca06d7SDimitry Andric }
45cfca06d7SDimitry Andric llvm_unreachable("unknown expression format");
46cfca06d7SDimitry Andric }
47cfca06d7SDimitry Andric
getWildcardRegex() const48b60736ecSDimitry Andric Expected<std::string> ExpressionFormat::getWildcardRegex() const {
49344a3780SDimitry Andric StringRef AlternateFormPrefix = AlternateForm ? StringRef("0x") : StringRef();
50344a3780SDimitry Andric
51344a3780SDimitry Andric auto CreatePrecisionRegex = [&](StringRef S) {
52344a3780SDimitry Andric return (Twine(AlternateFormPrefix) + S + Twine('{') + Twine(Precision) +
53344a3780SDimitry Andric "}")
54344a3780SDimitry Andric .str();
55b60736ecSDimitry Andric };
56b60736ecSDimitry Andric
57cfca06d7SDimitry Andric switch (Value) {
58cfca06d7SDimitry Andric case Kind::Unsigned:
59b60736ecSDimitry Andric if (Precision)
60b60736ecSDimitry Andric return CreatePrecisionRegex("([1-9][0-9]*)?[0-9]");
61b60736ecSDimitry Andric return std::string("[0-9]+");
62cfca06d7SDimitry Andric case Kind::Signed:
63b60736ecSDimitry Andric if (Precision)
64b60736ecSDimitry Andric return CreatePrecisionRegex("-?([1-9][0-9]*)?[0-9]");
65b60736ecSDimitry Andric return std::string("-?[0-9]+");
66cfca06d7SDimitry Andric case Kind::HexUpper:
67b60736ecSDimitry Andric if (Precision)
68b60736ecSDimitry Andric return CreatePrecisionRegex("([1-9A-F][0-9A-F]*)?[0-9A-F]");
69344a3780SDimitry Andric return (Twine(AlternateFormPrefix) + Twine("[0-9A-F]+")).str();
70cfca06d7SDimitry Andric case Kind::HexLower:
71b60736ecSDimitry Andric if (Precision)
72b60736ecSDimitry Andric return CreatePrecisionRegex("([1-9a-f][0-9a-f]*)?[0-9a-f]");
73344a3780SDimitry Andric return (Twine(AlternateFormPrefix) + Twine("[0-9a-f]+")).str();
74cfca06d7SDimitry Andric default:
75cfca06d7SDimitry Andric return createStringError(std::errc::invalid_argument,
76cfca06d7SDimitry Andric "trying to match value with invalid format");
77cfca06d7SDimitry Andric }
78cfca06d7SDimitry Andric }
79cfca06d7SDimitry Andric
80cfca06d7SDimitry Andric Expected<std::string>
getMatchingString(APInt IntValue) const81b1c73532SDimitry Andric ExpressionFormat::getMatchingString(APInt IntValue) const {
82b1c73532SDimitry Andric if (Value != Kind::Signed && IntValue.isNegative())
837fa27ce4SDimitry Andric return make_error<OverflowError>();
84b60736ecSDimitry Andric
857fa27ce4SDimitry Andric unsigned Radix;
867fa27ce4SDimitry Andric bool UpperCase = false;
877fa27ce4SDimitry Andric SmallString<8> AbsoluteValueStr;
887fa27ce4SDimitry Andric StringRef SignPrefix = IntValue.isNegative() ? "-" : "";
89cfca06d7SDimitry Andric switch (Value) {
90cfca06d7SDimitry Andric case Kind::Unsigned:
91b60736ecSDimitry Andric case Kind::Signed:
927fa27ce4SDimitry Andric Radix = 10;
93b60736ecSDimitry Andric break;
94cfca06d7SDimitry Andric case Kind::HexUpper:
957fa27ce4SDimitry Andric UpperCase = true;
967fa27ce4SDimitry Andric Radix = 16;
977fa27ce4SDimitry Andric break;
98cfca06d7SDimitry Andric case Kind::HexLower:
997fa27ce4SDimitry Andric Radix = 16;
1007fa27ce4SDimitry Andric UpperCase = false;
101b60736ecSDimitry Andric break;
102cfca06d7SDimitry Andric default:
103cfca06d7SDimitry Andric return createStringError(std::errc::invalid_argument,
104cfca06d7SDimitry Andric "trying to match value with invalid format");
105cfca06d7SDimitry Andric }
1067fa27ce4SDimitry Andric IntValue.abs().toString(AbsoluteValueStr, Radix, /*Signed=*/false,
1077fa27ce4SDimitry Andric /*formatAsCLiteral=*/false,
1087fa27ce4SDimitry Andric /*UpperCase=*/UpperCase);
109b60736ecSDimitry Andric
110344a3780SDimitry Andric StringRef AlternateFormPrefix = AlternateForm ? StringRef("0x") : StringRef();
111344a3780SDimitry Andric
112b60736ecSDimitry Andric if (Precision > AbsoluteValueStr.size()) {
113b60736ecSDimitry Andric unsigned LeadingZeros = Precision - AbsoluteValueStr.size();
114344a3780SDimitry Andric return (Twine(SignPrefix) + Twine(AlternateFormPrefix) +
115344a3780SDimitry Andric std::string(LeadingZeros, '0') + AbsoluteValueStr)
116b60736ecSDimitry Andric .str();
117b60736ecSDimitry Andric }
118b60736ecSDimitry Andric
119344a3780SDimitry Andric return (Twine(SignPrefix) + Twine(AlternateFormPrefix) + AbsoluteValueStr)
120344a3780SDimitry Andric .str();
121cfca06d7SDimitry Andric }
122cfca06d7SDimitry Andric
nextAPIntBitWidth(unsigned BitWidth)123b1c73532SDimitry Andric static unsigned nextAPIntBitWidth(unsigned BitWidth) {
124b1c73532SDimitry Andric return (BitWidth < APInt::APINT_BITS_PER_WORD) ? APInt::APINT_BITS_PER_WORD
125b1c73532SDimitry Andric : BitWidth * 2;
126b1c73532SDimitry Andric }
127b1c73532SDimitry Andric
toSigned(APInt AbsVal,bool Negative)128b1c73532SDimitry Andric static APInt toSigned(APInt AbsVal, bool Negative) {
129b1c73532SDimitry Andric if (AbsVal.isSignBitSet())
130b1c73532SDimitry Andric AbsVal = AbsVal.zext(nextAPIntBitWidth(AbsVal.getBitWidth()));
131b1c73532SDimitry Andric APInt Result = AbsVal;
132b1c73532SDimitry Andric if (Negative)
133b1c73532SDimitry Andric Result.negate();
134b1c73532SDimitry Andric return Result;
135b1c73532SDimitry Andric }
136b1c73532SDimitry Andric
valueFromStringRepr(StringRef StrVal,const SourceMgr & SM) const137b1c73532SDimitry Andric APInt ExpressionFormat::valueFromStringRepr(StringRef StrVal,
138cfca06d7SDimitry Andric const SourceMgr &SM) const {
139cfca06d7SDimitry Andric bool ValueIsSigned = Value == Kind::Signed;
140b1c73532SDimitry Andric bool Negative = StrVal.consume_front("-");
141cfca06d7SDimitry Andric bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
142b1c73532SDimitry Andric bool MissingFormPrefix =
143b1c73532SDimitry Andric !ValueIsSigned && AlternateForm && !StrVal.consume_front("0x");
1447fa27ce4SDimitry Andric (void)MissingFormPrefix;
1457fa27ce4SDimitry Andric assert(!MissingFormPrefix && "missing alternate form prefix");
146b1c73532SDimitry Andric APInt ResultValue;
147b1c73532SDimitry Andric [[maybe_unused]] bool ParseFailure =
148b1c73532SDimitry Andric StrVal.getAsInteger(Hex ? 16 : 10, ResultValue);
149b1c73532SDimitry Andric // Both the FileCheck utility and library only call this method with a valid
150b1c73532SDimitry Andric // value in StrVal. This is guaranteed by the regex returned by
151b1c73532SDimitry Andric // getWildcardRegex() above.
152b1c73532SDimitry Andric assert(!ParseFailure && "unable to represent numeric value");
153b1c73532SDimitry Andric return toSigned(ResultValue, Negative);
154cfca06d7SDimitry Andric }
155cfca06d7SDimitry Andric
exprAdd(const APInt & LeftOperand,const APInt & RightOperand,bool & Overflow)156b1c73532SDimitry Andric Expected<APInt> llvm::exprAdd(const APInt &LeftOperand,
157b1c73532SDimitry Andric const APInt &RightOperand, bool &Overflow) {
158b1c73532SDimitry Andric return LeftOperand.sadd_ov(RightOperand, Overflow);
159cfca06d7SDimitry Andric }
160cfca06d7SDimitry Andric
exprSub(const APInt & LeftOperand,const APInt & RightOperand,bool & Overflow)161b1c73532SDimitry Andric Expected<APInt> llvm::exprSub(const APInt &LeftOperand,
162b1c73532SDimitry Andric const APInt &RightOperand, bool &Overflow) {
163b1c73532SDimitry Andric return LeftOperand.ssub_ov(RightOperand, Overflow);
164cfca06d7SDimitry Andric }
165cfca06d7SDimitry Andric
exprMul(const APInt & LeftOperand,const APInt & RightOperand,bool & Overflow)166b1c73532SDimitry Andric Expected<APInt> llvm::exprMul(const APInt &LeftOperand,
167b1c73532SDimitry Andric const APInt &RightOperand, bool &Overflow) {
168b1c73532SDimitry Andric return LeftOperand.smul_ov(RightOperand, Overflow);
169cfca06d7SDimitry Andric }
170cfca06d7SDimitry Andric
exprDiv(const APInt & LeftOperand,const APInt & RightOperand,bool & Overflow)171b1c73532SDimitry Andric Expected<APInt> llvm::exprDiv(const APInt &LeftOperand,
172b1c73532SDimitry Andric const APInt &RightOperand, bool &Overflow) {
1737fa27ce4SDimitry Andric // Check for division by zero.
174b1c73532SDimitry Andric if (RightOperand.isZero())
175cfca06d7SDimitry Andric return make_error<OverflowError>();
176cfca06d7SDimitry Andric
177b1c73532SDimitry Andric return LeftOperand.sdiv_ov(RightOperand, Overflow);
178cfca06d7SDimitry Andric }
179cfca06d7SDimitry Andric
exprMax(const APInt & LeftOperand,const APInt & RightOperand,bool & Overflow)180b1c73532SDimitry Andric Expected<APInt> llvm::exprMax(const APInt &LeftOperand,
181b1c73532SDimitry Andric const APInt &RightOperand, bool &Overflow) {
182b1c73532SDimitry Andric Overflow = false;
183b1c73532SDimitry Andric return LeftOperand.slt(RightOperand) ? RightOperand : LeftOperand;
184cfca06d7SDimitry Andric }
185cfca06d7SDimitry Andric
exprMin(const APInt & LeftOperand,const APInt & RightOperand,bool & Overflow)186b1c73532SDimitry Andric Expected<APInt> llvm::exprMin(const APInt &LeftOperand,
187b1c73532SDimitry Andric const APInt &RightOperand, bool &Overflow) {
188b1c73532SDimitry Andric Overflow = false;
189b1c73532SDimitry Andric if (cantFail(exprMax(LeftOperand, RightOperand, Overflow)) == LeftOperand)
190cfca06d7SDimitry Andric return RightOperand;
191cfca06d7SDimitry Andric
192cfca06d7SDimitry Andric return LeftOperand;
193cfca06d7SDimitry Andric }
194cfca06d7SDimitry Andric
eval() const195b1c73532SDimitry Andric Expected<APInt> NumericVariableUse::eval() const {
196b1c73532SDimitry Andric std::optional<APInt> Value = Variable->getValue();
197e6d15924SDimitry Andric if (Value)
198e6d15924SDimitry Andric return *Value;
1991d5ae102SDimitry Andric
200cfca06d7SDimitry Andric return make_error<UndefVarError>(getExpressionStr());
201e6d15924SDimitry Andric }
202e6d15924SDimitry Andric
eval() const203b1c73532SDimitry Andric Expected<APInt> BinaryOperation::eval() const {
204b1c73532SDimitry Andric Expected<APInt> MaybeLeftOp = LeftOperand->eval();
205b1c73532SDimitry Andric Expected<APInt> MaybeRightOp = RightOperand->eval();
206e6d15924SDimitry Andric
207e6d15924SDimitry Andric // Bubble up any error (e.g. undefined variables) in the recursive
208e6d15924SDimitry Andric // evaluation.
209b1c73532SDimitry Andric if (!MaybeLeftOp || !MaybeRightOp) {
210e6d15924SDimitry Andric Error Err = Error::success();
211b1c73532SDimitry Andric if (!MaybeLeftOp)
212b1c73532SDimitry Andric Err = joinErrors(std::move(Err), MaybeLeftOp.takeError());
213b1c73532SDimitry Andric if (!MaybeRightOp)
214b1c73532SDimitry Andric Err = joinErrors(std::move(Err), MaybeRightOp.takeError());
215e6d15924SDimitry Andric return std::move(Err);
216e6d15924SDimitry Andric }
217e6d15924SDimitry Andric
218b1c73532SDimitry Andric APInt LeftOp = *MaybeLeftOp;
219b1c73532SDimitry Andric APInt RightOp = *MaybeRightOp;
220b1c73532SDimitry Andric bool Overflow;
221b1c73532SDimitry Andric // Ensure both operands have the same bitwidth.
222b1c73532SDimitry Andric unsigned LeftBitWidth = LeftOp.getBitWidth();
223b1c73532SDimitry Andric unsigned RightBitWidth = RightOp.getBitWidth();
224b1c73532SDimitry Andric unsigned NewBitWidth = std::max(LeftBitWidth, RightBitWidth);
225b1c73532SDimitry Andric LeftOp = LeftOp.sext(NewBitWidth);
226b1c73532SDimitry Andric RightOp = RightOp.sext(NewBitWidth);
227b1c73532SDimitry Andric do {
228b1c73532SDimitry Andric Expected<APInt> MaybeResult = EvalBinop(LeftOp, RightOp, Overflow);
229b1c73532SDimitry Andric if (!MaybeResult)
230b1c73532SDimitry Andric return MaybeResult.takeError();
231b1c73532SDimitry Andric
232b1c73532SDimitry Andric if (!Overflow)
233b1c73532SDimitry Andric return MaybeResult;
234b1c73532SDimitry Andric
235b1c73532SDimitry Andric NewBitWidth = nextAPIntBitWidth(NewBitWidth);
236b1c73532SDimitry Andric LeftOp = LeftOp.sext(NewBitWidth);
237b1c73532SDimitry Andric RightOp = RightOp.sext(NewBitWidth);
238b1c73532SDimitry Andric } while (true);
239e6d15924SDimitry Andric }
240e6d15924SDimitry Andric
241cfca06d7SDimitry Andric Expected<ExpressionFormat>
getImplicitFormat(const SourceMgr & SM) const242cfca06d7SDimitry Andric BinaryOperation::getImplicitFormat(const SourceMgr &SM) const {
243cfca06d7SDimitry Andric Expected<ExpressionFormat> LeftFormat = LeftOperand->getImplicitFormat(SM);
244cfca06d7SDimitry Andric Expected<ExpressionFormat> RightFormat = RightOperand->getImplicitFormat(SM);
245cfca06d7SDimitry Andric if (!LeftFormat || !RightFormat) {
246cfca06d7SDimitry Andric Error Err = Error::success();
247cfca06d7SDimitry Andric if (!LeftFormat)
248cfca06d7SDimitry Andric Err = joinErrors(std::move(Err), LeftFormat.takeError());
249cfca06d7SDimitry Andric if (!RightFormat)
250cfca06d7SDimitry Andric Err = joinErrors(std::move(Err), RightFormat.takeError());
251cfca06d7SDimitry Andric return std::move(Err);
252cfca06d7SDimitry Andric }
253cfca06d7SDimitry Andric
254cfca06d7SDimitry Andric if (*LeftFormat != ExpressionFormat::Kind::NoFormat &&
255cfca06d7SDimitry Andric *RightFormat != ExpressionFormat::Kind::NoFormat &&
256cfca06d7SDimitry Andric *LeftFormat != *RightFormat)
257cfca06d7SDimitry Andric return ErrorDiagnostic::get(
258cfca06d7SDimitry Andric SM, getExpressionStr(),
259cfca06d7SDimitry Andric "implicit format conflict between '" + LeftOperand->getExpressionStr() +
260cfca06d7SDimitry Andric "' (" + LeftFormat->toString() + ") and '" +
261cfca06d7SDimitry Andric RightOperand->getExpressionStr() + "' (" + RightFormat->toString() +
262cfca06d7SDimitry Andric "), need an explicit format specifier");
263cfca06d7SDimitry Andric
264cfca06d7SDimitry Andric return *LeftFormat != ExpressionFormat::Kind::NoFormat ? *LeftFormat
265cfca06d7SDimitry Andric : *RightFormat;
266cfca06d7SDimitry Andric }
267cfca06d7SDimitry Andric
getResult() const268706b4fc4SDimitry Andric Expected<std::string> NumericSubstitution::getResult() const {
269cfca06d7SDimitry Andric assert(ExpressionPointer->getAST() != nullptr &&
270cfca06d7SDimitry Andric "Substituting empty expression");
271b1c73532SDimitry Andric Expected<APInt> EvaluatedValue = ExpressionPointer->getAST()->eval();
272e6d15924SDimitry Andric if (!EvaluatedValue)
273e6d15924SDimitry Andric return EvaluatedValue.takeError();
274cfca06d7SDimitry Andric ExpressionFormat Format = ExpressionPointer->getFormat();
275cfca06d7SDimitry Andric return Format.getMatchingString(*EvaluatedValue);
276e6d15924SDimitry Andric }
277e6d15924SDimitry Andric
getResult() const278706b4fc4SDimitry Andric Expected<std::string> StringSubstitution::getResult() const {
279e6d15924SDimitry Andric // Look up the value and escape it so that we can put it into the regex.
280e6d15924SDimitry Andric Expected<StringRef> VarVal = Context->getPatternVarValue(FromStr);
281e6d15924SDimitry Andric if (!VarVal)
282e6d15924SDimitry Andric return VarVal.takeError();
283e6d15924SDimitry Andric return Regex::escape(*VarVal);
284e6d15924SDimitry Andric }
285e6d15924SDimitry Andric
isValidVarNameStart(char C)286cfca06d7SDimitry Andric bool Pattern::isValidVarNameStart(char C) { return C == '_' || isAlpha(C); }
287e6d15924SDimitry Andric
288706b4fc4SDimitry Andric Expected<Pattern::VariableProperties>
parseVariable(StringRef & Str,const SourceMgr & SM)289706b4fc4SDimitry Andric Pattern::parseVariable(StringRef &Str, const SourceMgr &SM) {
290e6d15924SDimitry Andric if (Str.empty())
291706b4fc4SDimitry Andric return ErrorDiagnostic::get(SM, Str, "empty variable name");
292e6d15924SDimitry Andric
293cfca06d7SDimitry Andric size_t I = 0;
294e6d15924SDimitry Andric bool IsPseudo = Str[0] == '@';
295e6d15924SDimitry Andric
296e6d15924SDimitry Andric // Global vars start with '$'.
297e6d15924SDimitry Andric if (Str[0] == '$' || IsPseudo)
298e6d15924SDimitry Andric ++I;
299e6d15924SDimitry Andric
300ac9a064cSDimitry Andric if (I == Str.size())
301ac9a064cSDimitry Andric return ErrorDiagnostic::get(SM, Str.slice(I, StringRef::npos),
302ac9a064cSDimitry Andric StringRef("empty ") +
303ac9a064cSDimitry Andric (IsPseudo ? "pseudo " : "global ") +
304ac9a064cSDimitry Andric "variable name");
305ac9a064cSDimitry Andric
306cfca06d7SDimitry Andric if (!isValidVarNameStart(Str[I++]))
307706b4fc4SDimitry Andric return ErrorDiagnostic::get(SM, Str, "invalid variable name");
308e6d15924SDimitry Andric
309cfca06d7SDimitry Andric for (size_t E = Str.size(); I != E; ++I)
310e6d15924SDimitry Andric // Variable names are composed of alphanumeric characters and underscores.
311cfca06d7SDimitry Andric if (Str[I] != '_' && !isAlnum(Str[I]))
312e6d15924SDimitry Andric break;
313e6d15924SDimitry Andric
314e6d15924SDimitry Andric StringRef Name = Str.take_front(I);
315e6d15924SDimitry Andric Str = Str.substr(I);
316e6d15924SDimitry Andric return VariableProperties {Name, IsPseudo};
317e6d15924SDimitry Andric }
318e6d15924SDimitry Andric
319e6d15924SDimitry Andric // StringRef holding all characters considered as horizontal whitespaces by
320e6d15924SDimitry Andric // FileCheck input canonicalization.
3211d5ae102SDimitry Andric constexpr StringLiteral SpaceChars = " \t";
322e6d15924SDimitry Andric
323e6d15924SDimitry Andric // Parsing helper function that strips the first character in S and returns it.
popFront(StringRef & S)324e6d15924SDimitry Andric static char popFront(StringRef &S) {
325e6d15924SDimitry Andric char C = S.front();
326e6d15924SDimitry Andric S = S.drop_front();
327e6d15924SDimitry Andric return C;
328e6d15924SDimitry Andric }
329e6d15924SDimitry Andric
330cfca06d7SDimitry Andric char OverflowError::ID = 0;
331706b4fc4SDimitry Andric char UndefVarError::ID = 0;
332706b4fc4SDimitry Andric char ErrorDiagnostic::ID = 0;
333706b4fc4SDimitry Andric char NotFoundError::ID = 0;
334344a3780SDimitry Andric char ErrorReported::ID = 0;
335e6d15924SDimitry Andric
parseNumericVariableDefinition(StringRef & Expr,FileCheckPatternContext * Context,std::optional<size_t> LineNumber,ExpressionFormat ImplicitFormat,const SourceMgr & SM)336706b4fc4SDimitry Andric Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
337e6d15924SDimitry Andric StringRef &Expr, FileCheckPatternContext *Context,
338e3b55780SDimitry Andric std::optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
339cfca06d7SDimitry Andric const SourceMgr &SM) {
340e6d15924SDimitry Andric Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
341e6d15924SDimitry Andric if (!ParseVarResult)
342e6d15924SDimitry Andric return ParseVarResult.takeError();
343e6d15924SDimitry Andric StringRef Name = ParseVarResult->Name;
344e6d15924SDimitry Andric
345e6d15924SDimitry Andric if (ParseVarResult->IsPseudo)
346706b4fc4SDimitry Andric return ErrorDiagnostic::get(
347e6d15924SDimitry Andric SM, Name, "definition of pseudo numeric variable unsupported");
348e6d15924SDimitry Andric
349e6d15924SDimitry Andric // Detect collisions between string and numeric variables when the latter
350e6d15924SDimitry Andric // is created later than the former.
3517fa27ce4SDimitry Andric if (Context->DefinedVariableTable.contains(Name))
352706b4fc4SDimitry Andric return ErrorDiagnostic::get(
353e6d15924SDimitry Andric SM, Name, "string variable with name '" + Name + "' already exists");
354e6d15924SDimitry Andric
355e6d15924SDimitry Andric Expr = Expr.ltrim(SpaceChars);
356e6d15924SDimitry Andric if (!Expr.empty())
357706b4fc4SDimitry Andric return ErrorDiagnostic::get(
358e6d15924SDimitry Andric SM, Expr, "unexpected characters after numeric variable name");
359e6d15924SDimitry Andric
360706b4fc4SDimitry Andric NumericVariable *DefinedNumericVariable;
361e6d15924SDimitry Andric auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
362cfca06d7SDimitry Andric if (VarTableIter != Context->GlobalNumericVariableTable.end()) {
363e6d15924SDimitry Andric DefinedNumericVariable = VarTableIter->second;
364cfca06d7SDimitry Andric if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat)
365cfca06d7SDimitry Andric return ErrorDiagnostic::get(
366cfca06d7SDimitry Andric SM, Expr, "format different from previous variable definition");
367cfca06d7SDimitry Andric } else
368cfca06d7SDimitry Andric DefinedNumericVariable =
369cfca06d7SDimitry Andric Context->makeNumericVariable(Name, ImplicitFormat, LineNumber);
370e6d15924SDimitry Andric
371e6d15924SDimitry Andric return DefinedNumericVariable;
372e6d15924SDimitry Andric }
373e6d15924SDimitry Andric
parseNumericVariableUse(StringRef Name,bool IsPseudo,std::optional<size_t> LineNumber,FileCheckPatternContext * Context,const SourceMgr & SM)374706b4fc4SDimitry Andric Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
375e3b55780SDimitry Andric StringRef Name, bool IsPseudo, std::optional<size_t> LineNumber,
376706b4fc4SDimitry Andric FileCheckPatternContext *Context, const SourceMgr &SM) {
377ac9a064cSDimitry Andric if (IsPseudo && Name != "@LINE")
378706b4fc4SDimitry Andric return ErrorDiagnostic::get(
379e6d15924SDimitry Andric SM, Name, "invalid pseudo numeric variable '" + Name + "'");
380e6d15924SDimitry Andric
381e6d15924SDimitry Andric // Numeric variable definitions and uses are parsed in the order in which
382e6d15924SDimitry Andric // they appear in the CHECK patterns. For each definition, the pointer to the
383e6d15924SDimitry Andric // class instance of the corresponding numeric variable definition is stored
384e6d15924SDimitry Andric // in GlobalNumericVariableTable in parsePattern. Therefore, if the pointer
385e6d15924SDimitry Andric // we get below is null, it means no such variable was defined before. When
386e6d15924SDimitry Andric // that happens, we create a dummy variable so that parsing can continue. All
387e6d15924SDimitry Andric // uses of undefined variables, whether string or numeric, are then diagnosed
388344a3780SDimitry Andric // in printNoMatch() after failing to match.
389e6d15924SDimitry Andric auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
390706b4fc4SDimitry Andric NumericVariable *NumericVariable;
391e6d15924SDimitry Andric if (VarTableIter != Context->GlobalNumericVariableTable.end())
392e6d15924SDimitry Andric NumericVariable = VarTableIter->second;
393e6d15924SDimitry Andric else {
394cfca06d7SDimitry Andric NumericVariable = Context->makeNumericVariable(
395cfca06d7SDimitry Andric Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
396e6d15924SDimitry Andric Context->GlobalNumericVariableTable[Name] = NumericVariable;
397e6d15924SDimitry Andric }
398e6d15924SDimitry Andric
399e3b55780SDimitry Andric std::optional<size_t> DefLineNumber = NumericVariable->getDefLineNumber();
400e6d15924SDimitry Andric if (DefLineNumber && LineNumber && *DefLineNumber == *LineNumber)
401706b4fc4SDimitry Andric return ErrorDiagnostic::get(
402e6d15924SDimitry Andric SM, Name,
4031d5ae102SDimitry Andric "numeric variable '" + Name +
4041d5ae102SDimitry Andric "' defined earlier in the same CHECK directive");
405e6d15924SDimitry Andric
406706b4fc4SDimitry Andric return std::make_unique<NumericVariableUse>(Name, NumericVariable);
407e6d15924SDimitry Andric }
408e6d15924SDimitry Andric
parseNumericOperand(StringRef & Expr,AllowedOperand AO,bool MaybeInvalidConstraint,std::optional<size_t> LineNumber,FileCheckPatternContext * Context,const SourceMgr & SM)409706b4fc4SDimitry Andric Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
410cfca06d7SDimitry Andric StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint,
411e3b55780SDimitry Andric std::optional<size_t> LineNumber, FileCheckPatternContext *Context,
412cfca06d7SDimitry Andric const SourceMgr &SM) {
413312c0ed1SDimitry Andric if (Expr.starts_with("(")) {
414cfca06d7SDimitry Andric if (AO != AllowedOperand::Any)
415cfca06d7SDimitry Andric return ErrorDiagnostic::get(
416cfca06d7SDimitry Andric SM, Expr, "parenthesized expression not permitted here");
417cfca06d7SDimitry Andric return parseParenExpr(Expr, LineNumber, Context, SM);
418cfca06d7SDimitry Andric }
419cfca06d7SDimitry Andric
420e6d15924SDimitry Andric if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {
421e6d15924SDimitry Andric // Try to parse as a numeric variable use.
422706b4fc4SDimitry Andric Expected<Pattern::VariableProperties> ParseVarResult =
423e6d15924SDimitry Andric parseVariable(Expr, SM);
424cfca06d7SDimitry Andric if (ParseVarResult) {
425cfca06d7SDimitry Andric // Try to parse a function call.
426312c0ed1SDimitry Andric if (Expr.ltrim(SpaceChars).starts_with("(")) {
427cfca06d7SDimitry Andric if (AO != AllowedOperand::Any)
428cfca06d7SDimitry Andric return ErrorDiagnostic::get(SM, ParseVarResult->Name,
429cfca06d7SDimitry Andric "unexpected function call");
430cfca06d7SDimitry Andric
431cfca06d7SDimitry Andric return parseCallExpr(Expr, ParseVarResult->Name, LineNumber, Context,
432cfca06d7SDimitry Andric SM);
433cfca06d7SDimitry Andric }
434cfca06d7SDimitry Andric
435e6d15924SDimitry Andric return parseNumericVariableUse(ParseVarResult->Name,
4361d5ae102SDimitry Andric ParseVarResult->IsPseudo, LineNumber,
4371d5ae102SDimitry Andric Context, SM);
438cfca06d7SDimitry Andric }
439cfca06d7SDimitry Andric
440e6d15924SDimitry Andric if (AO == AllowedOperand::LineVar)
441e6d15924SDimitry Andric return ParseVarResult.takeError();
442e6d15924SDimitry Andric // Ignore the error and retry parsing as a literal.
443e6d15924SDimitry Andric consumeError(ParseVarResult.takeError());
444e6d15924SDimitry Andric }
445e6d15924SDimitry Andric
446e6d15924SDimitry Andric // Otherwise, parse it as a literal.
447b1c73532SDimitry Andric APInt LiteralValue;
448cfca06d7SDimitry Andric StringRef SaveExpr = Expr;
449b1c73532SDimitry Andric bool Negative = Expr.consume_front("-");
450cfca06d7SDimitry Andric if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
451b1c73532SDimitry Andric LiteralValue)) {
452b1c73532SDimitry Andric LiteralValue = toSigned(LiteralValue, Negative);
453cfca06d7SDimitry Andric return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()),
454b1c73532SDimitry Andric LiteralValue);
455b1c73532SDimitry Andric }
456cfca06d7SDimitry Andric return ErrorDiagnostic::get(
457b1c73532SDimitry Andric SM, SaveExpr,
458cfca06d7SDimitry Andric Twine("invalid ") +
459cfca06d7SDimitry Andric (MaybeInvalidConstraint ? "matching constraint or " : "") +
460cfca06d7SDimitry Andric "operand format");
461e6d15924SDimitry Andric }
462e6d15924SDimitry Andric
463706b4fc4SDimitry Andric Expected<std::unique_ptr<ExpressionAST>>
parseParenExpr(StringRef & Expr,std::optional<size_t> LineNumber,FileCheckPatternContext * Context,const SourceMgr & SM)464e3b55780SDimitry Andric Pattern::parseParenExpr(StringRef &Expr, std::optional<size_t> LineNumber,
4651d5ae102SDimitry Andric FileCheckPatternContext *Context, const SourceMgr &SM) {
466e6d15924SDimitry Andric Expr = Expr.ltrim(SpaceChars);
467312c0ed1SDimitry Andric assert(Expr.starts_with("("));
468cfca06d7SDimitry Andric
469cfca06d7SDimitry Andric // Parse right operand.
470cfca06d7SDimitry Andric Expr.consume_front("(");
471cfca06d7SDimitry Andric Expr = Expr.ltrim(SpaceChars);
472e6d15924SDimitry Andric if (Expr.empty())
473cfca06d7SDimitry Andric return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
474cfca06d7SDimitry Andric
475cfca06d7SDimitry Andric // Note: parseNumericOperand handles nested opening parentheses.
476cfca06d7SDimitry Andric Expected<std::unique_ptr<ExpressionAST>> SubExprResult = parseNumericOperand(
477cfca06d7SDimitry Andric Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
478cfca06d7SDimitry Andric Context, SM);
479cfca06d7SDimitry Andric Expr = Expr.ltrim(SpaceChars);
480312c0ed1SDimitry Andric while (SubExprResult && !Expr.empty() && !Expr.starts_with(")")) {
481cfca06d7SDimitry Andric StringRef OrigExpr = Expr;
482cfca06d7SDimitry Andric SubExprResult = parseBinop(OrigExpr, Expr, std::move(*SubExprResult), false,
483cfca06d7SDimitry Andric LineNumber, Context, SM);
484cfca06d7SDimitry Andric Expr = Expr.ltrim(SpaceChars);
485cfca06d7SDimitry Andric }
486cfca06d7SDimitry Andric if (!SubExprResult)
487cfca06d7SDimitry Andric return SubExprResult;
488cfca06d7SDimitry Andric
489cfca06d7SDimitry Andric if (!Expr.consume_front(")")) {
490cfca06d7SDimitry Andric return ErrorDiagnostic::get(SM, Expr,
491cfca06d7SDimitry Andric "missing ')' at end of nested expression");
492cfca06d7SDimitry Andric }
493cfca06d7SDimitry Andric return SubExprResult;
494cfca06d7SDimitry Andric }
495cfca06d7SDimitry Andric
496cfca06d7SDimitry Andric Expected<std::unique_ptr<ExpressionAST>>
parseBinop(StringRef Expr,StringRef & RemainingExpr,std::unique_ptr<ExpressionAST> LeftOp,bool IsLegacyLineExpr,std::optional<size_t> LineNumber,FileCheckPatternContext * Context,const SourceMgr & SM)497cfca06d7SDimitry Andric Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
498cfca06d7SDimitry Andric std::unique_ptr<ExpressionAST> LeftOp,
499e3b55780SDimitry Andric bool IsLegacyLineExpr, std::optional<size_t> LineNumber,
500cfca06d7SDimitry Andric FileCheckPatternContext *Context, const SourceMgr &SM) {
501cfca06d7SDimitry Andric RemainingExpr = RemainingExpr.ltrim(SpaceChars);
502cfca06d7SDimitry Andric if (RemainingExpr.empty())
503e6d15924SDimitry Andric return std::move(LeftOp);
504e6d15924SDimitry Andric
505e6d15924SDimitry Andric // Check if this is a supported operation and select a function to perform
506e6d15924SDimitry Andric // it.
507cfca06d7SDimitry Andric SMLoc OpLoc = SMLoc::getFromPointer(RemainingExpr.data());
508cfca06d7SDimitry Andric char Operator = popFront(RemainingExpr);
509e6d15924SDimitry Andric binop_eval_t EvalBinop;
510e6d15924SDimitry Andric switch (Operator) {
511e6d15924SDimitry Andric case '+':
512b1c73532SDimitry Andric EvalBinop = exprAdd;
513e6d15924SDimitry Andric break;
514e6d15924SDimitry Andric case '-':
515b1c73532SDimitry Andric EvalBinop = exprSub;
516e6d15924SDimitry Andric break;
517e6d15924SDimitry Andric default:
518706b4fc4SDimitry Andric return ErrorDiagnostic::get(
519e6d15924SDimitry Andric SM, OpLoc, Twine("unsupported operation '") + Twine(Operator) + "'");
520e6d15924SDimitry Andric }
521e6d15924SDimitry Andric
522e6d15924SDimitry Andric // Parse right operand.
523cfca06d7SDimitry Andric RemainingExpr = RemainingExpr.ltrim(SpaceChars);
524cfca06d7SDimitry Andric if (RemainingExpr.empty())
525cfca06d7SDimitry Andric return ErrorDiagnostic::get(SM, RemainingExpr,
526cfca06d7SDimitry Andric "missing operand in expression");
527e6d15924SDimitry Andric // The second operand in a legacy @LINE expression is always a literal.
528e6d15924SDimitry Andric AllowedOperand AO =
529cfca06d7SDimitry Andric IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
530706b4fc4SDimitry Andric Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
531cfca06d7SDimitry Andric parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false,
532cfca06d7SDimitry Andric LineNumber, Context, SM);
533e6d15924SDimitry Andric if (!RightOpResult)
534e6d15924SDimitry Andric return RightOpResult;
535e6d15924SDimitry Andric
536cfca06d7SDimitry Andric Expr = Expr.drop_back(RemainingExpr.size());
537cfca06d7SDimitry Andric return std::make_unique<BinaryOperation>(Expr, EvalBinop, std::move(LeftOp),
538e6d15924SDimitry Andric std::move(*RightOpResult));
539e6d15924SDimitry Andric }
540e6d15924SDimitry Andric
541cfca06d7SDimitry Andric Expected<std::unique_ptr<ExpressionAST>>
parseCallExpr(StringRef & Expr,StringRef FuncName,std::optional<size_t> LineNumber,FileCheckPatternContext * Context,const SourceMgr & SM)542cfca06d7SDimitry Andric Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName,
543e3b55780SDimitry Andric std::optional<size_t> LineNumber,
544cfca06d7SDimitry Andric FileCheckPatternContext *Context, const SourceMgr &SM) {
545cfca06d7SDimitry Andric Expr = Expr.ltrim(SpaceChars);
546312c0ed1SDimitry Andric assert(Expr.starts_with("("));
547cfca06d7SDimitry Andric
548e3b55780SDimitry Andric auto OptFunc = StringSwitch<binop_eval_t>(FuncName)
549b1c73532SDimitry Andric .Case("add", exprAdd)
550b1c73532SDimitry Andric .Case("div", exprDiv)
551b1c73532SDimitry Andric .Case("max", exprMax)
552b1c73532SDimitry Andric .Case("min", exprMin)
553b1c73532SDimitry Andric .Case("mul", exprMul)
554b1c73532SDimitry Andric .Case("sub", exprSub)
555e3b55780SDimitry Andric .Default(nullptr);
556cfca06d7SDimitry Andric
557cfca06d7SDimitry Andric if (!OptFunc)
558cfca06d7SDimitry Andric return ErrorDiagnostic::get(
559cfca06d7SDimitry Andric SM, FuncName, Twine("call to undefined function '") + FuncName + "'");
560cfca06d7SDimitry Andric
561cfca06d7SDimitry Andric Expr.consume_front("(");
562cfca06d7SDimitry Andric Expr = Expr.ltrim(SpaceChars);
563cfca06d7SDimitry Andric
564cfca06d7SDimitry Andric // Parse call arguments, which are comma separated.
565cfca06d7SDimitry Andric SmallVector<std::unique_ptr<ExpressionAST>, 4> Args;
566312c0ed1SDimitry Andric while (!Expr.empty() && !Expr.starts_with(")")) {
567312c0ed1SDimitry Andric if (Expr.starts_with(","))
568cfca06d7SDimitry Andric return ErrorDiagnostic::get(SM, Expr, "missing argument");
569cfca06d7SDimitry Andric
570cfca06d7SDimitry Andric // Parse the argument, which is an arbitary expression.
571cfca06d7SDimitry Andric StringRef OuterBinOpExpr = Expr;
572cfca06d7SDimitry Andric Expected<std::unique_ptr<ExpressionAST>> Arg = parseNumericOperand(
573cfca06d7SDimitry Andric Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
574cfca06d7SDimitry Andric Context, SM);
575cfca06d7SDimitry Andric while (Arg && !Expr.empty()) {
576cfca06d7SDimitry Andric Expr = Expr.ltrim(SpaceChars);
577cfca06d7SDimitry Andric // Have we reached an argument terminator?
578312c0ed1SDimitry Andric if (Expr.starts_with(",") || Expr.starts_with(")"))
579cfca06d7SDimitry Andric break;
580cfca06d7SDimitry Andric
581cfca06d7SDimitry Andric // Arg = Arg <op> <expr>
582cfca06d7SDimitry Andric Arg = parseBinop(OuterBinOpExpr, Expr, std::move(*Arg), false, LineNumber,
583cfca06d7SDimitry Andric Context, SM);
584cfca06d7SDimitry Andric }
585cfca06d7SDimitry Andric
586cfca06d7SDimitry Andric // Prefer an expression error over a generic invalid argument message.
587cfca06d7SDimitry Andric if (!Arg)
588cfca06d7SDimitry Andric return Arg.takeError();
589cfca06d7SDimitry Andric Args.push_back(std::move(*Arg));
590cfca06d7SDimitry Andric
591cfca06d7SDimitry Andric // Have we parsed all available arguments?
592cfca06d7SDimitry Andric Expr = Expr.ltrim(SpaceChars);
593cfca06d7SDimitry Andric if (!Expr.consume_front(","))
594cfca06d7SDimitry Andric break;
595cfca06d7SDimitry Andric
596cfca06d7SDimitry Andric Expr = Expr.ltrim(SpaceChars);
597312c0ed1SDimitry Andric if (Expr.starts_with(")"))
598cfca06d7SDimitry Andric return ErrorDiagnostic::get(SM, Expr, "missing argument");
599cfca06d7SDimitry Andric }
600cfca06d7SDimitry Andric
601cfca06d7SDimitry Andric if (!Expr.consume_front(")"))
602cfca06d7SDimitry Andric return ErrorDiagnostic::get(SM, Expr,
603cfca06d7SDimitry Andric "missing ')' at end of call expression");
604cfca06d7SDimitry Andric
605cfca06d7SDimitry Andric const unsigned NumArgs = Args.size();
606cfca06d7SDimitry Andric if (NumArgs == 2)
607cfca06d7SDimitry Andric return std::make_unique<BinaryOperation>(Expr, *OptFunc, std::move(Args[0]),
608cfca06d7SDimitry Andric std::move(Args[1]));
609cfca06d7SDimitry Andric
610cfca06d7SDimitry Andric // TODO: Support more than binop_eval_t.
611cfca06d7SDimitry Andric return ErrorDiagnostic::get(SM, FuncName,
612cfca06d7SDimitry Andric Twine("function '") + FuncName +
613cfca06d7SDimitry Andric Twine("' takes 2 arguments but ") +
614cfca06d7SDimitry Andric Twine(NumArgs) + " given");
615cfca06d7SDimitry Andric }
616cfca06d7SDimitry Andric
parseNumericSubstitutionBlock(StringRef Expr,std::optional<NumericVariable * > & DefinedNumericVariable,bool IsLegacyLineExpr,std::optional<size_t> LineNumber,FileCheckPatternContext * Context,const SourceMgr & SM)617cfca06d7SDimitry Andric Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
618e3b55780SDimitry Andric StringRef Expr, std::optional<NumericVariable *> &DefinedNumericVariable,
619e3b55780SDimitry Andric bool IsLegacyLineExpr, std::optional<size_t> LineNumber,
6201d5ae102SDimitry Andric FileCheckPatternContext *Context, const SourceMgr &SM) {
621706b4fc4SDimitry Andric std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr;
6221d5ae102SDimitry Andric StringRef DefExpr = StringRef();
623e3b55780SDimitry Andric DefinedNumericVariable = std::nullopt;
624cfca06d7SDimitry Andric ExpressionFormat ExplicitFormat = ExpressionFormat();
625b60736ecSDimitry Andric unsigned Precision = 0;
626cfca06d7SDimitry Andric
627ac9a064cSDimitry Andric // Parse format specifier (NOTE: ',' is also an argument separator).
628cfca06d7SDimitry Andric size_t FormatSpecEnd = Expr.find(',');
629cfca06d7SDimitry Andric size_t FunctionStart = Expr.find('(');
630cfca06d7SDimitry Andric if (FormatSpecEnd != StringRef::npos && FormatSpecEnd < FunctionStart) {
631b60736ecSDimitry Andric StringRef FormatExpr = Expr.take_front(FormatSpecEnd);
632b60736ecSDimitry Andric Expr = Expr.drop_front(FormatSpecEnd + 1);
633b60736ecSDimitry Andric FormatExpr = FormatExpr.trim(SpaceChars);
634b60736ecSDimitry Andric if (!FormatExpr.consume_front("%"))
635cfca06d7SDimitry Andric return ErrorDiagnostic::get(
636b60736ecSDimitry Andric SM, FormatExpr,
637b60736ecSDimitry Andric "invalid matching format specification in expression");
638cfca06d7SDimitry Andric
639344a3780SDimitry Andric // Parse alternate form flag.
640344a3780SDimitry Andric SMLoc AlternateFormFlagLoc = SMLoc::getFromPointer(FormatExpr.data());
641344a3780SDimitry Andric bool AlternateForm = FormatExpr.consume_front("#");
642344a3780SDimitry Andric
643b60736ecSDimitry Andric // Parse precision.
644b60736ecSDimitry Andric if (FormatExpr.consume_front(".")) {
645b60736ecSDimitry Andric if (FormatExpr.consumeInteger(10, Precision))
646b60736ecSDimitry Andric return ErrorDiagnostic::get(SM, FormatExpr,
647b60736ecSDimitry Andric "invalid precision in format specifier");
648cfca06d7SDimitry Andric }
649cfca06d7SDimitry Andric
650b60736ecSDimitry Andric if (!FormatExpr.empty()) {
651b60736ecSDimitry Andric // Check for unknown matching format specifier and set matching format in
652b60736ecSDimitry Andric // class instance representing this expression.
653b60736ecSDimitry Andric SMLoc FmtLoc = SMLoc::getFromPointer(FormatExpr.data());
654b60736ecSDimitry Andric switch (popFront(FormatExpr)) {
655b60736ecSDimitry Andric case 'u':
656b60736ecSDimitry Andric ExplicitFormat =
657b60736ecSDimitry Andric ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision);
658b60736ecSDimitry Andric break;
659b60736ecSDimitry Andric case 'd':
660b60736ecSDimitry Andric ExplicitFormat =
661b60736ecSDimitry Andric ExpressionFormat(ExpressionFormat::Kind::Signed, Precision);
662b60736ecSDimitry Andric break;
663b60736ecSDimitry Andric case 'x':
664344a3780SDimitry Andric ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower,
665344a3780SDimitry Andric Precision, AlternateForm);
666b60736ecSDimitry Andric break;
667b60736ecSDimitry Andric case 'X':
668344a3780SDimitry Andric ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper,
669344a3780SDimitry Andric Precision, AlternateForm);
670b60736ecSDimitry Andric break;
671b60736ecSDimitry Andric default:
672b60736ecSDimitry Andric return ErrorDiagnostic::get(SM, FmtLoc,
673b60736ecSDimitry Andric "invalid format specifier in expression");
674b60736ecSDimitry Andric }
675b60736ecSDimitry Andric }
676b60736ecSDimitry Andric
677344a3780SDimitry Andric if (AlternateForm && ExplicitFormat != ExpressionFormat::Kind::HexLower &&
678344a3780SDimitry Andric ExplicitFormat != ExpressionFormat::Kind::HexUpper)
679344a3780SDimitry Andric return ErrorDiagnostic::get(
680344a3780SDimitry Andric SM, AlternateFormFlagLoc,
681344a3780SDimitry Andric "alternate form only supported for hex values");
682344a3780SDimitry Andric
683b60736ecSDimitry Andric FormatExpr = FormatExpr.ltrim(SpaceChars);
684b60736ecSDimitry Andric if (!FormatExpr.empty())
685cfca06d7SDimitry Andric return ErrorDiagnostic::get(
686b60736ecSDimitry Andric SM, FormatExpr,
687b60736ecSDimitry Andric "invalid matching format specification in expression");
688cfca06d7SDimitry Andric }
689cfca06d7SDimitry Andric
6901d5ae102SDimitry Andric // Save variable definition expression if any.
691e6d15924SDimitry Andric size_t DefEnd = Expr.find(':');
692e6d15924SDimitry Andric if (DefEnd != StringRef::npos) {
6931d5ae102SDimitry Andric DefExpr = Expr.substr(0, DefEnd);
6941d5ae102SDimitry Andric Expr = Expr.substr(DefEnd + 1);
695e6d15924SDimitry Andric }
696e6d15924SDimitry Andric
697cfca06d7SDimitry Andric // Parse matching constraint.
698cfca06d7SDimitry Andric Expr = Expr.ltrim(SpaceChars);
699ac9a064cSDimitry Andric bool HasParsedValidConstraint = Expr.consume_front("==");
700cfca06d7SDimitry Andric
701e6d15924SDimitry Andric // Parse the expression itself.
702e6d15924SDimitry Andric Expr = Expr.ltrim(SpaceChars);
703cfca06d7SDimitry Andric if (Expr.empty()) {
704cfca06d7SDimitry Andric if (HasParsedValidConstraint)
705cfca06d7SDimitry Andric return ErrorDiagnostic::get(
706cfca06d7SDimitry Andric SM, Expr, "empty numeric expression should not have a constraint");
707cfca06d7SDimitry Andric } else {
708cfca06d7SDimitry Andric Expr = Expr.rtrim(SpaceChars);
709cfca06d7SDimitry Andric StringRef OuterBinOpExpr = Expr;
7101d5ae102SDimitry Andric // The first operand in a legacy @LINE expression is always the @LINE
7111d5ae102SDimitry Andric // pseudo variable.
712e6d15924SDimitry Andric AllowedOperand AO =
713e6d15924SDimitry Andric IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;
714cfca06d7SDimitry Andric Expected<std::unique_ptr<ExpressionAST>> ParseResult = parseNumericOperand(
715cfca06d7SDimitry Andric Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM);
716e6d15924SDimitry Andric while (ParseResult && !Expr.empty()) {
717cfca06d7SDimitry Andric ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult),
718cfca06d7SDimitry Andric IsLegacyLineExpr, LineNumber, Context, SM);
719e6d15924SDimitry Andric // Legacy @LINE expressions only allow 2 operands.
720e6d15924SDimitry Andric if (ParseResult && IsLegacyLineExpr && !Expr.empty())
721706b4fc4SDimitry Andric return ErrorDiagnostic::get(
722e6d15924SDimitry Andric SM, Expr,
723e6d15924SDimitry Andric "unexpected characters at end of expression '" + Expr + "'");
724e6d15924SDimitry Andric }
725e6d15924SDimitry Andric if (!ParseResult)
726cfca06d7SDimitry Andric return ParseResult.takeError();
727706b4fc4SDimitry Andric ExpressionASTPointer = std::move(*ParseResult);
7281d5ae102SDimitry Andric }
7291d5ae102SDimitry Andric
730cfca06d7SDimitry Andric // Select format of the expression, i.e. (i) its explicit format, if any,
731cfca06d7SDimitry Andric // otherwise (ii) its implicit format, if any, otherwise (iii) the default
732cfca06d7SDimitry Andric // format (unsigned). Error out in case of conflicting implicit format
733cfca06d7SDimitry Andric // without explicit format.
734cfca06d7SDimitry Andric ExpressionFormat Format;
735cfca06d7SDimitry Andric if (ExplicitFormat)
736cfca06d7SDimitry Andric Format = ExplicitFormat;
737cfca06d7SDimitry Andric else if (ExpressionASTPointer) {
738cfca06d7SDimitry Andric Expected<ExpressionFormat> ImplicitFormat =
739cfca06d7SDimitry Andric ExpressionASTPointer->getImplicitFormat(SM);
740cfca06d7SDimitry Andric if (!ImplicitFormat)
741cfca06d7SDimitry Andric return ImplicitFormat.takeError();
742cfca06d7SDimitry Andric Format = *ImplicitFormat;
743cfca06d7SDimitry Andric }
744cfca06d7SDimitry Andric if (!Format)
745b60736ecSDimitry Andric Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision);
746cfca06d7SDimitry Andric
747cfca06d7SDimitry Andric std::unique_ptr<Expression> ExpressionPointer =
748cfca06d7SDimitry Andric std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);
749cfca06d7SDimitry Andric
7501d5ae102SDimitry Andric // Parse the numeric variable definition.
7511d5ae102SDimitry Andric if (DefEnd != StringRef::npos) {
7521d5ae102SDimitry Andric DefExpr = DefExpr.ltrim(SpaceChars);
753cfca06d7SDimitry Andric Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition(
754cfca06d7SDimitry Andric DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM);
7551d5ae102SDimitry Andric
7561d5ae102SDimitry Andric if (!ParseResult)
7571d5ae102SDimitry Andric return ParseResult.takeError();
7581d5ae102SDimitry Andric DefinedNumericVariable = *ParseResult;
7591d5ae102SDimitry Andric }
7601d5ae102SDimitry Andric
761cfca06d7SDimitry Andric return std::move(ExpressionPointer);
762e6d15924SDimitry Andric }
763e6d15924SDimitry Andric
parsePattern(StringRef PatternStr,StringRef Prefix,SourceMgr & SM,const FileCheckRequest & Req)764706b4fc4SDimitry Andric bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
765706b4fc4SDimitry Andric SourceMgr &SM, const FileCheckRequest &Req) {
766d8e91e46SDimitry Andric bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
7671d5ae102SDimitry Andric IgnoreCase = Req.IgnoreCase;
768d8e91e46SDimitry Andric
769d8e91e46SDimitry Andric PatternLoc = SMLoc::getFromPointer(PatternStr.data());
770d8e91e46SDimitry Andric
771d8e91e46SDimitry Andric if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
772d8e91e46SDimitry Andric // Ignore trailing whitespace.
773ac9a064cSDimitry Andric PatternStr = PatternStr.rtrim(" \t");
774d8e91e46SDimitry Andric
775d8e91e46SDimitry Andric // Check that there is something on the line.
776d8e91e46SDimitry Andric if (PatternStr.empty() && CheckTy != Check::CheckEmpty) {
777d8e91e46SDimitry Andric SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,
778d8e91e46SDimitry Andric "found empty check string with prefix '" + Prefix + ":'");
779d8e91e46SDimitry Andric return true;
780d8e91e46SDimitry Andric }
781d8e91e46SDimitry Andric
782d8e91e46SDimitry Andric if (!PatternStr.empty() && CheckTy == Check::CheckEmpty) {
783d8e91e46SDimitry Andric SM.PrintMessage(
784d8e91e46SDimitry Andric PatternLoc, SourceMgr::DK_Error,
785d8e91e46SDimitry Andric "found non-empty check string for empty check with prefix '" + Prefix +
786d8e91e46SDimitry Andric ":'");
787d8e91e46SDimitry Andric return true;
788d8e91e46SDimitry Andric }
789d8e91e46SDimitry Andric
790d8e91e46SDimitry Andric if (CheckTy == Check::CheckEmpty) {
791d8e91e46SDimitry Andric RegExStr = "(\n$)";
792d8e91e46SDimitry Andric return false;
793d8e91e46SDimitry Andric }
794d8e91e46SDimitry Andric
795b60736ecSDimitry Andric // If literal check, set fixed string.
796b60736ecSDimitry Andric if (CheckTy.isLiteralMatch()) {
797b60736ecSDimitry Andric FixedStr = PatternStr;
798b60736ecSDimitry Andric return false;
799b60736ecSDimitry Andric }
800b60736ecSDimitry Andric
801d8e91e46SDimitry Andric // Check to see if this is a fixed string, or if it has regex pieces.
802d8e91e46SDimitry Andric if (!MatchFullLinesHere &&
803c0981da4SDimitry Andric (PatternStr.size() < 2 ||
804c0981da4SDimitry Andric (!PatternStr.contains("{{") && !PatternStr.contains("[[")))) {
805d8e91e46SDimitry Andric FixedStr = PatternStr;
806d8e91e46SDimitry Andric return false;
807d8e91e46SDimitry Andric }
808d8e91e46SDimitry Andric
809d8e91e46SDimitry Andric if (MatchFullLinesHere) {
810d8e91e46SDimitry Andric RegExStr += '^';
811d8e91e46SDimitry Andric if (!Req.NoCanonicalizeWhiteSpace)
812d8e91e46SDimitry Andric RegExStr += " *";
813d8e91e46SDimitry Andric }
814d8e91e46SDimitry Andric
815d8e91e46SDimitry Andric // Paren value #0 is for the fully matched string. Any new parenthesized
816d8e91e46SDimitry Andric // values add from there.
817d8e91e46SDimitry Andric unsigned CurParen = 1;
818d8e91e46SDimitry Andric
819d8e91e46SDimitry Andric // Otherwise, there is at least one regex piece. Build up the regex pattern
820d8e91e46SDimitry Andric // by escaping scary characters in fixed strings, building up one big regex.
821d8e91e46SDimitry Andric while (!PatternStr.empty()) {
822d8e91e46SDimitry Andric // RegEx matches.
823312c0ed1SDimitry Andric if (PatternStr.starts_with("{{")) {
824d8e91e46SDimitry Andric // This is the start of a regex match. Scan for the }}.
825d8e91e46SDimitry Andric size_t End = PatternStr.find("}}");
826d8e91e46SDimitry Andric if (End == StringRef::npos) {
827d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
828d8e91e46SDimitry Andric SourceMgr::DK_Error,
829d8e91e46SDimitry Andric "found start of regex string with no end '}}'");
830d8e91e46SDimitry Andric return true;
831d8e91e46SDimitry Andric }
832d8e91e46SDimitry Andric
833d8e91e46SDimitry Andric // Enclose {{}} patterns in parens just like [[]] even though we're not
834d8e91e46SDimitry Andric // capturing the result for any purpose. This is required in case the
835d8e91e46SDimitry Andric // expression contains an alternation like: CHECK: abc{{x|z}}def. We
836d8e91e46SDimitry Andric // want this to turn into: "abc(x|z)def" not "abcx|zdef".
837b1c73532SDimitry Andric bool HasAlternation = PatternStr.contains('|');
838b1c73532SDimitry Andric if (HasAlternation) {
839d8e91e46SDimitry Andric RegExStr += '(';
840d8e91e46SDimitry Andric ++CurParen;
841b1c73532SDimitry Andric }
842d8e91e46SDimitry Andric
843d8e91e46SDimitry Andric if (AddRegExToRegEx(PatternStr.substr(2, End - 2), CurParen, SM))
844d8e91e46SDimitry Andric return true;
845b1c73532SDimitry Andric if (HasAlternation)
846d8e91e46SDimitry Andric RegExStr += ')';
847d8e91e46SDimitry Andric
848d8e91e46SDimitry Andric PatternStr = PatternStr.substr(End + 2);
849d8e91e46SDimitry Andric continue;
850d8e91e46SDimitry Andric }
851d8e91e46SDimitry Andric
8521d5ae102SDimitry Andric // String and numeric substitution blocks. Pattern substitution blocks come
853e6d15924SDimitry Andric // in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some
854e6d15924SDimitry Andric // other regex) and assigns it to the string variable 'foo'. The latter
8551d5ae102SDimitry Andric // substitutes foo's value. Numeric substitution blocks recognize the same
8561d5ae102SDimitry Andric // form as string ones, but start with a '#' sign after the double
8571d5ae102SDimitry Andric // brackets. They also accept a combined form which sets a numeric variable
8581d5ae102SDimitry Andric // to the evaluation of an expression. Both string and numeric variable
8591d5ae102SDimitry Andric // names must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*" to be
8606f8fc217SDimitry Andric // valid, as this helps catch some common errors. If there are extra '['s
8616f8fc217SDimitry Andric // before the "[[", treat them literally.
862312c0ed1SDimitry Andric if (PatternStr.starts_with("[[") && !PatternStr.starts_with("[[[")) {
863e6d15924SDimitry Andric StringRef UnparsedPatternStr = PatternStr.substr(2);
864d8e91e46SDimitry Andric // Find the closing bracket pair ending the match. End is going to be an
865d8e91e46SDimitry Andric // offset relative to the beginning of the match string.
866e6d15924SDimitry Andric size_t End = FindRegexVarEnd(UnparsedPatternStr, SM);
867e6d15924SDimitry Andric StringRef MatchStr = UnparsedPatternStr.substr(0, End);
868e6d15924SDimitry Andric bool IsNumBlock = MatchStr.consume_front("#");
869d8e91e46SDimitry Andric
870d8e91e46SDimitry Andric if (End == StringRef::npos) {
871d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),
872d8e91e46SDimitry Andric SourceMgr::DK_Error,
873e6d15924SDimitry Andric "Invalid substitution block, no ]] found");
874e6d15924SDimitry Andric return true;
875e6d15924SDimitry Andric }
876e6d15924SDimitry Andric // Strip the substitution block we are parsing. End points to the start
877e6d15924SDimitry Andric // of the "]]" closing the expression so account for it in computing the
878e6d15924SDimitry Andric // index of the first unparsed character.
879e6d15924SDimitry Andric PatternStr = UnparsedPatternStr.substr(End + 2);
880e6d15924SDimitry Andric
881e6d15924SDimitry Andric bool IsDefinition = false;
8821d5ae102SDimitry Andric bool SubstNeeded = false;
883e6d15924SDimitry Andric // Whether the substitution block is a legacy use of @LINE with string
884e6d15924SDimitry Andric // substitution block syntax.
885e6d15924SDimitry Andric bool IsLegacyLineExpr = false;
886e6d15924SDimitry Andric StringRef DefName;
887e6d15924SDimitry Andric StringRef SubstStr;
888c0981da4SDimitry Andric StringRef MatchRegexp;
889c0981da4SDimitry Andric std::string WildcardRegexp;
890e6d15924SDimitry Andric size_t SubstInsertIdx = RegExStr.size();
891e6d15924SDimitry Andric
892e6d15924SDimitry Andric // Parse string variable or legacy @LINE expression.
893e6d15924SDimitry Andric if (!IsNumBlock) {
894b60736ecSDimitry Andric size_t VarEndIdx = MatchStr.find(':');
895e6d15924SDimitry Andric size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");
896e6d15924SDimitry Andric if (SpacePos != StringRef::npos) {
897e6d15924SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),
898e6d15924SDimitry Andric SourceMgr::DK_Error, "unexpected whitespace");
899d8e91e46SDimitry Andric return true;
900d8e91e46SDimitry Andric }
901d8e91e46SDimitry Andric
902e6d15924SDimitry Andric // Get the name (e.g. "foo") and verify it is well formed.
903e6d15924SDimitry Andric StringRef OrigMatchStr = MatchStr;
904706b4fc4SDimitry Andric Expected<Pattern::VariableProperties> ParseVarResult =
905e6d15924SDimitry Andric parseVariable(MatchStr, SM);
906e6d15924SDimitry Andric if (!ParseVarResult) {
907e6d15924SDimitry Andric logAllUnhandledErrors(ParseVarResult.takeError(), errs());
908d8e91e46SDimitry Andric return true;
909d8e91e46SDimitry Andric }
910e6d15924SDimitry Andric StringRef Name = ParseVarResult->Name;
911e6d15924SDimitry Andric bool IsPseudo = ParseVarResult->IsPseudo;
912d8e91e46SDimitry Andric
913e6d15924SDimitry Andric IsDefinition = (VarEndIdx != StringRef::npos);
9141d5ae102SDimitry Andric SubstNeeded = !IsDefinition;
915e6d15924SDimitry Andric if (IsDefinition) {
916e6d15924SDimitry Andric if ((IsPseudo || !MatchStr.consume_front(":"))) {
917d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
918d8e91e46SDimitry Andric SourceMgr::DK_Error,
919e6d15924SDimitry Andric "invalid name in string variable definition");
920d8e91e46SDimitry Andric return true;
921d8e91e46SDimitry Andric }
922d8e91e46SDimitry Andric
923e6d15924SDimitry Andric // Detect collisions between string and numeric variables when the
924e6d15924SDimitry Andric // former is created later than the latter.
9257fa27ce4SDimitry Andric if (Context->GlobalNumericVariableTable.contains(Name)) {
926e6d15924SDimitry Andric SM.PrintMessage(
927e6d15924SDimitry Andric SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
928e6d15924SDimitry Andric "numeric variable with name '" + Name + "' already exists");
929e6d15924SDimitry Andric return true;
930e6d15924SDimitry Andric }
931e6d15924SDimitry Andric DefName = Name;
932c0981da4SDimitry Andric MatchRegexp = MatchStr;
933e6d15924SDimitry Andric } else {
934e6d15924SDimitry Andric if (IsPseudo) {
935e6d15924SDimitry Andric MatchStr = OrigMatchStr;
936e6d15924SDimitry Andric IsLegacyLineExpr = IsNumBlock = true;
937344a3780SDimitry Andric } else {
938344a3780SDimitry Andric if (!MatchStr.empty()) {
939344a3780SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Name.data()),
940344a3780SDimitry Andric SourceMgr::DK_Error,
941344a3780SDimitry Andric "invalid name in string variable use");
942344a3780SDimitry Andric return true;
943344a3780SDimitry Andric }
944e6d15924SDimitry Andric SubstStr = Name;
945e6d15924SDimitry Andric }
946e6d15924SDimitry Andric }
947344a3780SDimitry Andric }
948e6d15924SDimitry Andric
949e6d15924SDimitry Andric // Parse numeric substitution block.
950cfca06d7SDimitry Andric std::unique_ptr<Expression> ExpressionPointer;
951e3b55780SDimitry Andric std::optional<NumericVariable *> DefinedNumericVariable;
952e6d15924SDimitry Andric if (IsNumBlock) {
953cfca06d7SDimitry Andric Expected<std::unique_ptr<Expression>> ParseResult =
954e6d15924SDimitry Andric parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
9551d5ae102SDimitry Andric IsLegacyLineExpr, LineNumber, Context,
9561d5ae102SDimitry Andric SM);
957e6d15924SDimitry Andric if (!ParseResult) {
958e6d15924SDimitry Andric logAllUnhandledErrors(ParseResult.takeError(), errs());
959e6d15924SDimitry Andric return true;
960e6d15924SDimitry Andric }
961cfca06d7SDimitry Andric ExpressionPointer = std::move(*ParseResult);
962cfca06d7SDimitry Andric SubstNeeded = ExpressionPointer->getAST() != nullptr;
963e6d15924SDimitry Andric if (DefinedNumericVariable) {
964e6d15924SDimitry Andric IsDefinition = true;
965e6d15924SDimitry Andric DefName = (*DefinedNumericVariable)->getName();
9661d5ae102SDimitry Andric }
9671d5ae102SDimitry Andric if (SubstNeeded)
968e6d15924SDimitry Andric SubstStr = MatchStr;
969cfca06d7SDimitry Andric else {
970cfca06d7SDimitry Andric ExpressionFormat Format = ExpressionPointer->getFormat();
971c0981da4SDimitry Andric WildcardRegexp = cantFail(Format.getWildcardRegex());
972c0981da4SDimitry Andric MatchRegexp = WildcardRegexp;
973cfca06d7SDimitry Andric }
974e6d15924SDimitry Andric }
975e6d15924SDimitry Andric
9761d5ae102SDimitry Andric // Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].
9771d5ae102SDimitry Andric if (IsDefinition) {
9781d5ae102SDimitry Andric RegExStr += '(';
9791d5ae102SDimitry Andric ++SubstInsertIdx;
9801d5ae102SDimitry Andric
9811d5ae102SDimitry Andric if (IsNumBlock) {
982706b4fc4SDimitry Andric NumericVariableMatch NumericVariableDefinition = {
9831d5ae102SDimitry Andric *DefinedNumericVariable, CurParen};
9841d5ae102SDimitry Andric NumericVariableDefs[DefName] = NumericVariableDefinition;
9851d5ae102SDimitry Andric // This store is done here rather than in match() to allow
9861d5ae102SDimitry Andric // parseNumericVariableUse() to get the pointer to the class instance
9871d5ae102SDimitry Andric // of the right variable definition corresponding to a given numeric
9881d5ae102SDimitry Andric // variable use.
9891d5ae102SDimitry Andric Context->GlobalNumericVariableTable[DefName] =
9901d5ae102SDimitry Andric *DefinedNumericVariable;
9911d5ae102SDimitry Andric } else {
9921d5ae102SDimitry Andric VariableDefs[DefName] = CurParen;
9931d5ae102SDimitry Andric // Mark string variable as defined to detect collisions between
9941d5ae102SDimitry Andric // string and numeric variables in parseNumericVariableUse() and
9951d5ae102SDimitry Andric // defineCmdlineVariables() when the latter is created later than the
9961d5ae102SDimitry Andric // former. We cannot reuse GlobalVariableTable for this by populating
9971d5ae102SDimitry Andric // it with an empty string since we would then lose the ability to
9981d5ae102SDimitry Andric // detect the use of an undefined variable in match().
9991d5ae102SDimitry Andric Context->DefinedVariableTable[DefName] = true;
10001d5ae102SDimitry Andric }
10011d5ae102SDimitry Andric
10021d5ae102SDimitry Andric ++CurParen;
10031d5ae102SDimitry Andric }
10041d5ae102SDimitry Andric
10051d5ae102SDimitry Andric if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM))
10061d5ae102SDimitry Andric return true;
10071d5ae102SDimitry Andric
10081d5ae102SDimitry Andric if (IsDefinition)
10091d5ae102SDimitry Andric RegExStr += ')';
10101d5ae102SDimitry Andric
1011e6d15924SDimitry Andric // Handle substitutions: [[foo]] and [[#<foo expr>]].
10121d5ae102SDimitry Andric if (SubstNeeded) {
1013e6d15924SDimitry Andric // Handle substitution of string variables that were defined earlier on
1014e6d15924SDimitry Andric // the same line by emitting a backreference. Expressions do not
1015e6d15924SDimitry Andric // support substituting a numeric variable defined on the same line.
1016e6d15924SDimitry Andric if (!IsNumBlock && VariableDefs.find(SubstStr) != VariableDefs.end()) {
1017e6d15924SDimitry Andric unsigned CaptureParenGroup = VariableDefs[SubstStr];
1018e6d15924SDimitry Andric if (CaptureParenGroup < 1 || CaptureParenGroup > 9) {
1019e6d15924SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(SubstStr.data()),
1020d8e91e46SDimitry Andric SourceMgr::DK_Error,
1021d8e91e46SDimitry Andric "Can't back-reference more than 9 variables");
1022d8e91e46SDimitry Andric return true;
1023d8e91e46SDimitry Andric }
1024e6d15924SDimitry Andric AddBackrefToRegEx(CaptureParenGroup);
1025d8e91e46SDimitry Andric } else {
1026e6d15924SDimitry Andric // Handle substitution of string variables ([[<var>]]) defined in
1027e6d15924SDimitry Andric // previous CHECK patterns, and substitution of expressions.
1028706b4fc4SDimitry Andric Substitution *Substitution =
1029e6d15924SDimitry Andric IsNumBlock
1030e6d15924SDimitry Andric ? Context->makeNumericSubstitution(
1031cfca06d7SDimitry Andric SubstStr, std::move(ExpressionPointer), SubstInsertIdx)
1032e6d15924SDimitry Andric : Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
1033e6d15924SDimitry Andric Substitutions.push_back(Substitution);
1034d8e91e46SDimitry Andric }
1035d8e91e46SDimitry Andric }
10366f8fc217SDimitry Andric
10376f8fc217SDimitry Andric continue;
1038d8e91e46SDimitry Andric }
1039d8e91e46SDimitry Andric
1040d8e91e46SDimitry Andric // Handle fixed string matches.
1041d8e91e46SDimitry Andric // Find the end, which is the start of the next regex.
10426f8fc217SDimitry Andric size_t FixedMatchEnd =
10436f8fc217SDimitry Andric std::min(PatternStr.find("{{", 1), PatternStr.find("[[", 1));
1044d8e91e46SDimitry Andric RegExStr += Regex::escape(PatternStr.substr(0, FixedMatchEnd));
1045d8e91e46SDimitry Andric PatternStr = PatternStr.substr(FixedMatchEnd);
1046d8e91e46SDimitry Andric }
1047d8e91e46SDimitry Andric
1048d8e91e46SDimitry Andric if (MatchFullLinesHere) {
1049d8e91e46SDimitry Andric if (!Req.NoCanonicalizeWhiteSpace)
1050d8e91e46SDimitry Andric RegExStr += " *";
1051d8e91e46SDimitry Andric RegExStr += '$';
1052d8e91e46SDimitry Andric }
1053d8e91e46SDimitry Andric
1054d8e91e46SDimitry Andric return false;
1055d8e91e46SDimitry Andric }
1056d8e91e46SDimitry Andric
AddRegExToRegEx(StringRef RS,unsigned & CurParen,SourceMgr & SM)1057706b4fc4SDimitry Andric bool Pattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM) {
1058d8e91e46SDimitry Andric Regex R(RS);
1059d8e91e46SDimitry Andric std::string Error;
1060d8e91e46SDimitry Andric if (!R.isValid(Error)) {
1061d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(RS.data()), SourceMgr::DK_Error,
1062d8e91e46SDimitry Andric "invalid regex: " + Error);
1063d8e91e46SDimitry Andric return true;
1064d8e91e46SDimitry Andric }
1065d8e91e46SDimitry Andric
1066d8e91e46SDimitry Andric RegExStr += RS.str();
1067d8e91e46SDimitry Andric CurParen += R.getNumMatches();
1068d8e91e46SDimitry Andric return false;
1069d8e91e46SDimitry Andric }
1070d8e91e46SDimitry Andric
AddBackrefToRegEx(unsigned BackrefNum)1071706b4fc4SDimitry Andric void Pattern::AddBackrefToRegEx(unsigned BackrefNum) {
1072d8e91e46SDimitry Andric assert(BackrefNum >= 1 && BackrefNum <= 9 && "Invalid backref number");
1073d8e91e46SDimitry Andric std::string Backref = std::string("\\") + std::string(1, '0' + BackrefNum);
1074d8e91e46SDimitry Andric RegExStr += Backref;
1075d8e91e46SDimitry Andric }
1076d8e91e46SDimitry Andric
match(StringRef Buffer,const SourceMgr & SM) const1077344a3780SDimitry Andric Pattern::MatchResult Pattern::match(StringRef Buffer,
1078e6d15924SDimitry Andric const SourceMgr &SM) const {
1079d8e91e46SDimitry Andric // If this is the EOF pattern, match it immediately.
1080344a3780SDimitry Andric if (CheckTy == Check::CheckEOF)
1081344a3780SDimitry Andric return MatchResult(Buffer.size(), 0, Error::success());
1082d8e91e46SDimitry Andric
1083d8e91e46SDimitry Andric // If this is a fixed string pattern, just match it now.
1084d8e91e46SDimitry Andric if (!FixedStr.empty()) {
1085706b4fc4SDimitry Andric size_t Pos =
1086344a3780SDimitry Andric IgnoreCase ? Buffer.find_insensitive(FixedStr) : Buffer.find(FixedStr);
1087e6d15924SDimitry Andric if (Pos == StringRef::npos)
1088706b4fc4SDimitry Andric return make_error<NotFoundError>();
1089344a3780SDimitry Andric return MatchResult(Pos, /*MatchLen=*/FixedStr.size(), Error::success());
1090d8e91e46SDimitry Andric }
1091d8e91e46SDimitry Andric
1092d8e91e46SDimitry Andric // Regex match.
1093d8e91e46SDimitry Andric
1094e6d15924SDimitry Andric // If there are substitutions, we need to create a temporary string with the
1095d8e91e46SDimitry Andric // actual value.
1096d8e91e46SDimitry Andric StringRef RegExToMatch = RegExStr;
1097d8e91e46SDimitry Andric std::string TmpStr;
1098e6d15924SDimitry Andric if (!Substitutions.empty()) {
1099d8e91e46SDimitry Andric TmpStr = RegExStr;
1100e6d15924SDimitry Andric if (LineNumber)
1101b1c73532SDimitry Andric Context->LineVariable->setValue(
1102b1c73532SDimitry Andric APInt(sizeof(*LineNumber) * 8, *LineNumber));
1103d8e91e46SDimitry Andric
1104e6d15924SDimitry Andric size_t InsertOffset = 0;
1105e6d15924SDimitry Andric // Substitute all string variables and expressions whose values are only
1106e6d15924SDimitry Andric // now known. Use of string variables defined on the same line are handled
1107e6d15924SDimitry Andric // by back-references.
1108344a3780SDimitry Andric Error Errs = Error::success();
1109e6d15924SDimitry Andric for (const auto &Substitution : Substitutions) {
1110e6d15924SDimitry Andric // Substitute and check for failure (e.g. use of undefined variable).
1111e6d15924SDimitry Andric Expected<std::string> Value = Substitution->getResult();
1112cfca06d7SDimitry Andric if (!Value) {
1113cfca06d7SDimitry Andric // Convert to an ErrorDiagnostic to get location information. This is
1114344a3780SDimitry Andric // done here rather than printMatch/printNoMatch since now we know which
1115cfca06d7SDimitry Andric // substitution block caused the overflow.
1116344a3780SDimitry Andric Errs = joinErrors(std::move(Errs),
1117344a3780SDimitry Andric handleErrors(
1118344a3780SDimitry Andric Value.takeError(),
1119344a3780SDimitry Andric [&](const OverflowError &E) {
1120344a3780SDimitry Andric return ErrorDiagnostic::get(
1121344a3780SDimitry Andric SM, Substitution->getFromString(),
1122cfca06d7SDimitry Andric "unable to substitute variable or "
1123cfca06d7SDimitry Andric "numeric expression: overflow error");
1124344a3780SDimitry Andric },
1125344a3780SDimitry Andric [&SM](const UndefVarError &E) {
1126344a3780SDimitry Andric return ErrorDiagnostic::get(SM, E.getVarName(),
1127344a3780SDimitry Andric E.message());
1128344a3780SDimitry Andric }));
1129344a3780SDimitry Andric continue;
1130cfca06d7SDimitry Andric }
1131d8e91e46SDimitry Andric
1132d8e91e46SDimitry Andric // Plop it into the regex at the adjusted offset.
1133e6d15924SDimitry Andric TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset,
1134e6d15924SDimitry Andric Value->begin(), Value->end());
1135e6d15924SDimitry Andric InsertOffset += Value->size();
1136d8e91e46SDimitry Andric }
1137344a3780SDimitry Andric if (Errs)
1138344a3780SDimitry Andric return std::move(Errs);
1139d8e91e46SDimitry Andric
1140d8e91e46SDimitry Andric // Match the newly constructed regex.
1141d8e91e46SDimitry Andric RegExToMatch = TmpStr;
1142d8e91e46SDimitry Andric }
1143d8e91e46SDimitry Andric
1144d8e91e46SDimitry Andric SmallVector<StringRef, 4> MatchInfo;
11451d5ae102SDimitry Andric unsigned int Flags = Regex::Newline;
11461d5ae102SDimitry Andric if (IgnoreCase)
11471d5ae102SDimitry Andric Flags |= Regex::IgnoreCase;
11481d5ae102SDimitry Andric if (!Regex(RegExToMatch, Flags).match(Buffer, &MatchInfo))
1149706b4fc4SDimitry Andric return make_error<NotFoundError>();
1150d8e91e46SDimitry Andric
1151d8e91e46SDimitry Andric // Successful regex match.
1152d8e91e46SDimitry Andric assert(!MatchInfo.empty() && "Didn't get any match");
1153d8e91e46SDimitry Andric StringRef FullMatch = MatchInfo[0];
1154d8e91e46SDimitry Andric
1155e6d15924SDimitry Andric // If this defines any string variables, remember their values.
1156d8e91e46SDimitry Andric for (const auto &VariableDef : VariableDefs) {
1157d8e91e46SDimitry Andric assert(VariableDef.second < MatchInfo.size() && "Internal paren error");
1158e6d15924SDimitry Andric Context->GlobalVariableTable[VariableDef.first] =
1159e6d15924SDimitry Andric MatchInfo[VariableDef.second];
1160e6d15924SDimitry Andric }
1161e6d15924SDimitry Andric
1162344a3780SDimitry Andric // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
1163344a3780SDimitry Andric // the required preceding newline, which is consumed by the pattern in the
1164344a3780SDimitry Andric // case of CHECK-EMPTY but not CHECK-NEXT.
1165344a3780SDimitry Andric size_t MatchStartSkip = CheckTy == Check::CheckEmpty;
1166344a3780SDimitry Andric Match TheMatch;
1167344a3780SDimitry Andric TheMatch.Pos = FullMatch.data() - Buffer.data() + MatchStartSkip;
1168344a3780SDimitry Andric TheMatch.Len = FullMatch.size() - MatchStartSkip;
1169344a3780SDimitry Andric
1170e6d15924SDimitry Andric // If this defines any numeric variables, remember their values.
1171e6d15924SDimitry Andric for (const auto &NumericVariableDef : NumericVariableDefs) {
1172706b4fc4SDimitry Andric const NumericVariableMatch &NumericVariableMatch =
1173e6d15924SDimitry Andric NumericVariableDef.getValue();
1174e6d15924SDimitry Andric unsigned CaptureParenGroup = NumericVariableMatch.CaptureParenGroup;
1175e6d15924SDimitry Andric assert(CaptureParenGroup < MatchInfo.size() && "Internal paren error");
1176706b4fc4SDimitry Andric NumericVariable *DefinedNumericVariable =
1177e6d15924SDimitry Andric NumericVariableMatch.DefinedNumericVariable;
1178e6d15924SDimitry Andric
1179e6d15924SDimitry Andric StringRef MatchedValue = MatchInfo[CaptureParenGroup];
1180cfca06d7SDimitry Andric ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat();
1181b1c73532SDimitry Andric APInt Value = Format.valueFromStringRepr(MatchedValue, SM);
1182b1c73532SDimitry Andric DefinedNumericVariable->setValue(Value, MatchedValue);
1183d8e91e46SDimitry Andric }
1184d8e91e46SDimitry Andric
1185344a3780SDimitry Andric return MatchResult(TheMatch, Error::success());
1186d8e91e46SDimitry Andric }
1187d8e91e46SDimitry Andric
computeMatchDistance(StringRef Buffer) const1188706b4fc4SDimitry Andric unsigned Pattern::computeMatchDistance(StringRef Buffer) const {
1189d8e91e46SDimitry Andric // Just compute the number of matching characters. For regular expressions, we
1190d8e91e46SDimitry Andric // just compare against the regex itself and hope for the best.
1191d8e91e46SDimitry Andric //
1192d8e91e46SDimitry Andric // FIXME: One easy improvement here is have the regex lib generate a single
1193d8e91e46SDimitry Andric // example regular expression which matches, and use that as the example
1194d8e91e46SDimitry Andric // string.
1195d8e91e46SDimitry Andric StringRef ExampleString(FixedStr);
1196d8e91e46SDimitry Andric if (ExampleString.empty())
1197d8e91e46SDimitry Andric ExampleString = RegExStr;
1198d8e91e46SDimitry Andric
1199d8e91e46SDimitry Andric // Only compare up to the first line in the buffer, or the string size.
1200d8e91e46SDimitry Andric StringRef BufferPrefix = Buffer.substr(0, ExampleString.size());
1201d8e91e46SDimitry Andric BufferPrefix = BufferPrefix.split('\n').first;
1202d8e91e46SDimitry Andric return BufferPrefix.edit_distance(ExampleString);
1203d8e91e46SDimitry Andric }
1204d8e91e46SDimitry Andric
printSubstitutions(const SourceMgr & SM,StringRef Buffer,SMRange Range,FileCheckDiag::MatchType MatchTy,std::vector<FileCheckDiag> * Diags) const1205706b4fc4SDimitry Andric void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
1206b60736ecSDimitry Andric SMRange Range,
1207b60736ecSDimitry Andric FileCheckDiag::MatchType MatchTy,
1208b60736ecSDimitry Andric std::vector<FileCheckDiag> *Diags) const {
1209e6d15924SDimitry Andric // Print what we know about substitutions.
1210e6d15924SDimitry Andric if (!Substitutions.empty()) {
1211e6d15924SDimitry Andric for (const auto &Substitution : Substitutions) {
1212d8e91e46SDimitry Andric SmallString<256> Msg;
1213d8e91e46SDimitry Andric raw_svector_ostream OS(Msg);
1214d8e91e46SDimitry Andric
1215344a3780SDimitry Andric Expected<std::string> MatchedValue = Substitution->getResult();
1216344a3780SDimitry Andric // Substitution failures are handled in printNoMatch().
1217e6d15924SDimitry Andric if (!MatchedValue) {
1218344a3780SDimitry Andric consumeError(MatchedValue.takeError());
1219344a3780SDimitry Andric continue;
1220d8e91e46SDimitry Andric }
1221344a3780SDimitry Andric
1222e6d15924SDimitry Andric OS << "with \"";
1223e6d15924SDimitry Andric OS.write_escaped(Substitution->getFromString()) << "\" equal to \"";
1224e6d15924SDimitry Andric OS.write_escaped(*MatchedValue) << "\"";
1225d8e91e46SDimitry Andric
1226b60736ecSDimitry Andric // We report only the start of the match/search range to suggest we are
1227b60736ecSDimitry Andric // reporting the substitutions as set at the start of the match/search.
1228b60736ecSDimitry Andric // Indicating a non-zero-length range might instead seem to imply that the
1229b60736ecSDimitry Andric // substitution matches or was captured from exactly that range.
1230b60736ecSDimitry Andric if (Diags)
1231b60736ecSDimitry Andric Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy,
1232b60736ecSDimitry Andric SMRange(Range.Start, Range.Start), OS.str());
1233d8e91e46SDimitry Andric else
1234b60736ecSDimitry Andric SM.PrintMessage(Range.Start, SourceMgr::DK_Note, OS.str());
1235d8e91e46SDimitry Andric }
1236d8e91e46SDimitry Andric }
1237d8e91e46SDimitry Andric }
1238d8e91e46SDimitry Andric
printVariableDefs(const SourceMgr & SM,FileCheckDiag::MatchType MatchTy,std::vector<FileCheckDiag> * Diags) const1239b60736ecSDimitry Andric void Pattern::printVariableDefs(const SourceMgr &SM,
1240b60736ecSDimitry Andric FileCheckDiag::MatchType MatchTy,
1241b60736ecSDimitry Andric std::vector<FileCheckDiag> *Diags) const {
1242b60736ecSDimitry Andric if (VariableDefs.empty() && NumericVariableDefs.empty())
1243b60736ecSDimitry Andric return;
1244b60736ecSDimitry Andric // Build list of variable captures.
1245b60736ecSDimitry Andric struct VarCapture {
1246b60736ecSDimitry Andric StringRef Name;
1247b60736ecSDimitry Andric SMRange Range;
1248b60736ecSDimitry Andric };
1249b60736ecSDimitry Andric SmallVector<VarCapture, 2> VarCaptures;
1250b60736ecSDimitry Andric for (const auto &VariableDef : VariableDefs) {
1251b60736ecSDimitry Andric VarCapture VC;
1252b60736ecSDimitry Andric VC.Name = VariableDef.first;
1253b60736ecSDimitry Andric StringRef Value = Context->GlobalVariableTable[VC.Name];
1254b60736ecSDimitry Andric SMLoc Start = SMLoc::getFromPointer(Value.data());
1255b60736ecSDimitry Andric SMLoc End = SMLoc::getFromPointer(Value.data() + Value.size());
1256b60736ecSDimitry Andric VC.Range = SMRange(Start, End);
1257b60736ecSDimitry Andric VarCaptures.push_back(VC);
1258b60736ecSDimitry Andric }
1259b60736ecSDimitry Andric for (const auto &VariableDef : NumericVariableDefs) {
1260b60736ecSDimitry Andric VarCapture VC;
1261b60736ecSDimitry Andric VC.Name = VariableDef.getKey();
1262e3b55780SDimitry Andric std::optional<StringRef> StrValue =
1263344a3780SDimitry Andric VariableDef.getValue().DefinedNumericVariable->getStringValue();
1264344a3780SDimitry Andric if (!StrValue)
1265344a3780SDimitry Andric continue;
1266344a3780SDimitry Andric SMLoc Start = SMLoc::getFromPointer(StrValue->data());
1267344a3780SDimitry Andric SMLoc End = SMLoc::getFromPointer(StrValue->data() + StrValue->size());
1268b60736ecSDimitry Andric VC.Range = SMRange(Start, End);
1269b60736ecSDimitry Andric VarCaptures.push_back(VC);
1270b60736ecSDimitry Andric }
1271b60736ecSDimitry Andric // Sort variable captures by the order in which they matched the input.
1272b60736ecSDimitry Andric // Ranges shouldn't be overlapping, so we can just compare the start.
1273b60736ecSDimitry Andric llvm::sort(VarCaptures, [](const VarCapture &A, const VarCapture &B) {
127408e8dd7bSDimitry Andric if (&A == &B)
127508e8dd7bSDimitry Andric return false;
1276b60736ecSDimitry Andric assert(A.Range.Start != B.Range.Start &&
1277b60736ecSDimitry Andric "unexpected overlapping variable captures");
1278b60736ecSDimitry Andric return A.Range.Start.getPointer() < B.Range.Start.getPointer();
1279b60736ecSDimitry Andric });
1280b60736ecSDimitry Andric // Create notes for the sorted captures.
1281b60736ecSDimitry Andric for (const VarCapture &VC : VarCaptures) {
1282b60736ecSDimitry Andric SmallString<256> Msg;
1283b60736ecSDimitry Andric raw_svector_ostream OS(Msg);
1284b60736ecSDimitry Andric OS << "captured var \"" << VC.Name << "\"";
1285b60736ecSDimitry Andric if (Diags)
1286b60736ecSDimitry Andric Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy, VC.Range, OS.str());
1287b60736ecSDimitry Andric else
1288b60736ecSDimitry Andric SM.PrintMessage(VC.Range.Start, SourceMgr::DK_Note, OS.str(), VC.Range);
1289b60736ecSDimitry Andric }
1290b60736ecSDimitry Andric }
1291b60736ecSDimitry Andric
ProcessMatchResult(FileCheckDiag::MatchType MatchTy,const SourceMgr & SM,SMLoc Loc,Check::FileCheckType CheckTy,StringRef Buffer,size_t Pos,size_t Len,std::vector<FileCheckDiag> * Diags,bool AdjustPrevDiags=false)1292d8e91e46SDimitry Andric static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,
1293d8e91e46SDimitry Andric const SourceMgr &SM, SMLoc Loc,
1294d8e91e46SDimitry Andric Check::FileCheckType CheckTy,
1295d8e91e46SDimitry Andric StringRef Buffer, size_t Pos, size_t Len,
1296d8e91e46SDimitry Andric std::vector<FileCheckDiag> *Diags,
1297b60736ecSDimitry Andric bool AdjustPrevDiags = false) {
1298d8e91e46SDimitry Andric SMLoc Start = SMLoc::getFromPointer(Buffer.data() + Pos);
1299d8e91e46SDimitry Andric SMLoc End = SMLoc::getFromPointer(Buffer.data() + Pos + Len);
1300d8e91e46SDimitry Andric SMRange Range(Start, End);
1301d8e91e46SDimitry Andric if (Diags) {
1302b60736ecSDimitry Andric if (AdjustPrevDiags) {
1303b60736ecSDimitry Andric SMLoc CheckLoc = Diags->rbegin()->CheckLoc;
1304b60736ecSDimitry Andric for (auto I = Diags->rbegin(), E = Diags->rend();
1305b60736ecSDimitry Andric I != E && I->CheckLoc == CheckLoc; ++I)
1306b60736ecSDimitry Andric I->MatchTy = MatchTy;
1307b60736ecSDimitry Andric } else
1308d8e91e46SDimitry Andric Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range);
1309d8e91e46SDimitry Andric }
1310d8e91e46SDimitry Andric return Range;
1311d8e91e46SDimitry Andric }
1312d8e91e46SDimitry Andric
printFuzzyMatch(const SourceMgr & SM,StringRef Buffer,std::vector<FileCheckDiag> * Diags) const1313706b4fc4SDimitry Andric void Pattern::printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
1314d8e91e46SDimitry Andric std::vector<FileCheckDiag> *Diags) const {
1315d8e91e46SDimitry Andric // Attempt to find the closest/best fuzzy match. Usually an error happens
1316d8e91e46SDimitry Andric // because some string in the output didn't exactly match. In these cases, we
1317d8e91e46SDimitry Andric // would like to show the user a best guess at what "should have" matched, to
1318d8e91e46SDimitry Andric // save them having to actually check the input manually.
1319d8e91e46SDimitry Andric size_t NumLinesForward = 0;
1320d8e91e46SDimitry Andric size_t Best = StringRef::npos;
1321d8e91e46SDimitry Andric double BestQuality = 0;
1322d8e91e46SDimitry Andric
1323d8e91e46SDimitry Andric // Use an arbitrary 4k limit on how far we will search.
1324d8e91e46SDimitry Andric for (size_t i = 0, e = std::min(size_t(4096), Buffer.size()); i != e; ++i) {
1325d8e91e46SDimitry Andric if (Buffer[i] == '\n')
1326d8e91e46SDimitry Andric ++NumLinesForward;
1327d8e91e46SDimitry Andric
1328d8e91e46SDimitry Andric // Patterns have leading whitespace stripped, so skip whitespace when
1329d8e91e46SDimitry Andric // looking for something which looks like a pattern.
1330d8e91e46SDimitry Andric if (Buffer[i] == ' ' || Buffer[i] == '\t')
1331d8e91e46SDimitry Andric continue;
1332d8e91e46SDimitry Andric
1333d8e91e46SDimitry Andric // Compute the "quality" of this match as an arbitrary combination of the
1334d8e91e46SDimitry Andric // match distance and the number of lines skipped to get to this match.
1335e6d15924SDimitry Andric unsigned Distance = computeMatchDistance(Buffer.substr(i));
1336d8e91e46SDimitry Andric double Quality = Distance + (NumLinesForward / 100.);
1337d8e91e46SDimitry Andric
1338d8e91e46SDimitry Andric if (Quality < BestQuality || Best == StringRef::npos) {
1339d8e91e46SDimitry Andric Best = i;
1340d8e91e46SDimitry Andric BestQuality = Quality;
1341d8e91e46SDimitry Andric }
1342d8e91e46SDimitry Andric }
1343d8e91e46SDimitry Andric
1344d8e91e46SDimitry Andric // Print the "possible intended match here" line if we found something
1345d8e91e46SDimitry Andric // reasonable and not equal to what we showed in the "scanning from here"
1346d8e91e46SDimitry Andric // line.
1347d8e91e46SDimitry Andric if (Best && Best != StringRef::npos && BestQuality < 50) {
1348d8e91e46SDimitry Andric SMRange MatchRange =
1349d8e91e46SDimitry Andric ProcessMatchResult(FileCheckDiag::MatchFuzzy, SM, getLoc(),
1350d8e91e46SDimitry Andric getCheckTy(), Buffer, Best, 0, Diags);
1351d8e91e46SDimitry Andric SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note,
1352d8e91e46SDimitry Andric "possible intended match here");
1353d8e91e46SDimitry Andric
1354d8e91e46SDimitry Andric // FIXME: If we wanted to be really friendly we would show why the match
1355d8e91e46SDimitry Andric // failed, as it can be hard to spot simple one character differences.
1356d8e91e46SDimitry Andric }
1357d8e91e46SDimitry Andric }
1358d8e91e46SDimitry Andric
1359e6d15924SDimitry Andric Expected<StringRef>
getPatternVarValue(StringRef VarName)1360e6d15924SDimitry Andric FileCheckPatternContext::getPatternVarValue(StringRef VarName) {
1361e6d15924SDimitry Andric auto VarIter = GlobalVariableTable.find(VarName);
1362e6d15924SDimitry Andric if (VarIter == GlobalVariableTable.end())
1363706b4fc4SDimitry Andric return make_error<UndefVarError>(VarName);
1364e6d15924SDimitry Andric
1365e6d15924SDimitry Andric return VarIter->second;
1366e6d15924SDimitry Andric }
1367e6d15924SDimitry Andric
1368e6d15924SDimitry Andric template <class... Types>
makeNumericVariable(Types...args)1369706b4fc4SDimitry Andric NumericVariable *FileCheckPatternContext::makeNumericVariable(Types... args) {
1370706b4fc4SDimitry Andric NumericVariables.push_back(std::make_unique<NumericVariable>(args...));
1371e6d15924SDimitry Andric return NumericVariables.back().get();
1372e6d15924SDimitry Andric }
1373e6d15924SDimitry Andric
1374706b4fc4SDimitry Andric Substitution *
makeStringSubstitution(StringRef VarName,size_t InsertIdx)1375e6d15924SDimitry Andric FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
1376e6d15924SDimitry Andric size_t InsertIdx) {
1377e6d15924SDimitry Andric Substitutions.push_back(
1378706b4fc4SDimitry Andric std::make_unique<StringSubstitution>(this, VarName, InsertIdx));
1379e6d15924SDimitry Andric return Substitutions.back().get();
1380e6d15924SDimitry Andric }
1381e6d15924SDimitry Andric
makeNumericSubstitution(StringRef ExpressionStr,std::unique_ptr<Expression> Expression,size_t InsertIdx)1382706b4fc4SDimitry Andric Substitution *FileCheckPatternContext::makeNumericSubstitution(
1383cfca06d7SDimitry Andric StringRef ExpressionStr, std::unique_ptr<Expression> Expression,
1384cfca06d7SDimitry Andric size_t InsertIdx) {
1385706b4fc4SDimitry Andric Substitutions.push_back(std::make_unique<NumericSubstitution>(
1386cfca06d7SDimitry Andric this, ExpressionStr, std::move(Expression), InsertIdx));
1387e6d15924SDimitry Andric return Substitutions.back().get();
1388e6d15924SDimitry Andric }
1389e6d15924SDimitry Andric
FindRegexVarEnd(StringRef Str,SourceMgr & SM)1390706b4fc4SDimitry Andric size_t Pattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {
1391d8e91e46SDimitry Andric // Offset keeps track of the current offset within the input Str
1392d8e91e46SDimitry Andric size_t Offset = 0;
1393d8e91e46SDimitry Andric // [...] Nesting depth
1394d8e91e46SDimitry Andric size_t BracketDepth = 0;
1395d8e91e46SDimitry Andric
1396d8e91e46SDimitry Andric while (!Str.empty()) {
1397312c0ed1SDimitry Andric if (Str.starts_with("]]") && BracketDepth == 0)
1398d8e91e46SDimitry Andric return Offset;
1399d8e91e46SDimitry Andric if (Str[0] == '\\') {
1400d8e91e46SDimitry Andric // Backslash escapes the next char within regexes, so skip them both.
1401d8e91e46SDimitry Andric Str = Str.substr(2);
1402d8e91e46SDimitry Andric Offset += 2;
1403d8e91e46SDimitry Andric } else {
1404d8e91e46SDimitry Andric switch (Str[0]) {
1405d8e91e46SDimitry Andric default:
1406d8e91e46SDimitry Andric break;
1407d8e91e46SDimitry Andric case '[':
1408d8e91e46SDimitry Andric BracketDepth++;
1409d8e91e46SDimitry Andric break;
1410d8e91e46SDimitry Andric case ']':
1411d8e91e46SDimitry Andric if (BracketDepth == 0) {
1412d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Str.data()),
1413d8e91e46SDimitry Andric SourceMgr::DK_Error,
1414d8e91e46SDimitry Andric "missing closing \"]\" for regex variable");
1415d8e91e46SDimitry Andric exit(1);
1416d8e91e46SDimitry Andric }
1417d8e91e46SDimitry Andric BracketDepth--;
1418d8e91e46SDimitry Andric break;
1419d8e91e46SDimitry Andric }
1420d8e91e46SDimitry Andric Str = Str.substr(1);
1421d8e91e46SDimitry Andric Offset++;
1422d8e91e46SDimitry Andric }
1423d8e91e46SDimitry Andric }
1424d8e91e46SDimitry Andric
1425d8e91e46SDimitry Andric return StringRef::npos;
1426d8e91e46SDimitry Andric }
1427d8e91e46SDimitry Andric
CanonicalizeFile(MemoryBuffer & MB,SmallVectorImpl<char> & OutputBuffer)1428e6d15924SDimitry Andric StringRef FileCheck::CanonicalizeFile(MemoryBuffer &MB,
1429d8e91e46SDimitry Andric SmallVectorImpl<char> &OutputBuffer) {
1430d8e91e46SDimitry Andric OutputBuffer.reserve(MB.getBufferSize());
1431d8e91e46SDimitry Andric
1432d8e91e46SDimitry Andric for (const char *Ptr = MB.getBufferStart(), *End = MB.getBufferEnd();
1433d8e91e46SDimitry Andric Ptr != End; ++Ptr) {
1434d8e91e46SDimitry Andric // Eliminate trailing dosish \r.
1435d8e91e46SDimitry Andric if (Ptr <= End - 2 && Ptr[0] == '\r' && Ptr[1] == '\n') {
1436d8e91e46SDimitry Andric continue;
1437d8e91e46SDimitry Andric }
1438d8e91e46SDimitry Andric
1439d8e91e46SDimitry Andric // If current char is not a horizontal whitespace or if horizontal
1440d8e91e46SDimitry Andric // whitespace canonicalization is disabled, dump it to output as is.
1441d8e91e46SDimitry Andric if (Req.NoCanonicalizeWhiteSpace || (*Ptr != ' ' && *Ptr != '\t')) {
1442d8e91e46SDimitry Andric OutputBuffer.push_back(*Ptr);
1443d8e91e46SDimitry Andric continue;
1444d8e91e46SDimitry Andric }
1445d8e91e46SDimitry Andric
1446d8e91e46SDimitry Andric // Otherwise, add one space and advance over neighboring space.
1447d8e91e46SDimitry Andric OutputBuffer.push_back(' ');
1448d8e91e46SDimitry Andric while (Ptr + 1 != End && (Ptr[1] == ' ' || Ptr[1] == '\t'))
1449d8e91e46SDimitry Andric ++Ptr;
1450d8e91e46SDimitry Andric }
1451d8e91e46SDimitry Andric
1452d8e91e46SDimitry Andric // Add a null byte and then return all but that byte.
1453d8e91e46SDimitry Andric OutputBuffer.push_back('\0');
1454d8e91e46SDimitry Andric return StringRef(OutputBuffer.data(), OutputBuffer.size() - 1);
1455d8e91e46SDimitry Andric }
1456d8e91e46SDimitry Andric
FileCheckDiag(const SourceMgr & SM,const Check::FileCheckType & CheckTy,SMLoc CheckLoc,MatchType MatchTy,SMRange InputRange,StringRef Note)1457d8e91e46SDimitry Andric FileCheckDiag::FileCheckDiag(const SourceMgr &SM,
1458d8e91e46SDimitry Andric const Check::FileCheckType &CheckTy,
1459d8e91e46SDimitry Andric SMLoc CheckLoc, MatchType MatchTy,
1460b60736ecSDimitry Andric SMRange InputRange, StringRef Note)
1461b60736ecSDimitry Andric : CheckTy(CheckTy), CheckLoc(CheckLoc), MatchTy(MatchTy), Note(Note) {
1462d8e91e46SDimitry Andric auto Start = SM.getLineAndColumn(InputRange.Start);
1463d8e91e46SDimitry Andric auto End = SM.getLineAndColumn(InputRange.End);
1464d8e91e46SDimitry Andric InputStartLine = Start.first;
1465d8e91e46SDimitry Andric InputStartCol = Start.second;
1466d8e91e46SDimitry Andric InputEndLine = End.first;
1467d8e91e46SDimitry Andric InputEndCol = End.second;
1468d8e91e46SDimitry Andric }
1469d8e91e46SDimitry Andric
IsPartOfWord(char c)1470d8e91e46SDimitry Andric static bool IsPartOfWord(char c) {
1471cfca06d7SDimitry Andric return (isAlnum(c) || c == '-' || c == '_');
1472d8e91e46SDimitry Andric }
1473d8e91e46SDimitry Andric
setCount(int C)1474d8e91e46SDimitry Andric Check::FileCheckType &Check::FileCheckType::setCount(int C) {
1475d8e91e46SDimitry Andric assert(Count > 0 && "zero and negative counts are not supported");
1476d8e91e46SDimitry Andric assert((C == 1 || Kind == CheckPlain) &&
1477d8e91e46SDimitry Andric "count supported only for plain CHECK directives");
1478d8e91e46SDimitry Andric Count = C;
1479d8e91e46SDimitry Andric return *this;
1480d8e91e46SDimitry Andric }
1481d8e91e46SDimitry Andric
getModifiersDescription() const1482b60736ecSDimitry Andric std::string Check::FileCheckType::getModifiersDescription() const {
1483b60736ecSDimitry Andric if (Modifiers.none())
1484b60736ecSDimitry Andric return "";
1485b60736ecSDimitry Andric std::string Ret;
1486b60736ecSDimitry Andric raw_string_ostream OS(Ret);
1487b60736ecSDimitry Andric OS << '{';
1488b60736ecSDimitry Andric if (isLiteralMatch())
1489b60736ecSDimitry Andric OS << "LITERAL";
1490b60736ecSDimitry Andric OS << '}';
1491ac9a064cSDimitry Andric return Ret;
1492b60736ecSDimitry Andric }
1493b60736ecSDimitry Andric
getDescription(StringRef Prefix) const1494d8e91e46SDimitry Andric std::string Check::FileCheckType::getDescription(StringRef Prefix) const {
1495b60736ecSDimitry Andric // Append directive modifiers.
1496b60736ecSDimitry Andric auto WithModifiers = [this, Prefix](StringRef Str) -> std::string {
1497b60736ecSDimitry Andric return (Prefix + Str + getModifiersDescription()).str();
1498b60736ecSDimitry Andric };
1499b60736ecSDimitry Andric
1500d8e91e46SDimitry Andric switch (Kind) {
1501d8e91e46SDimitry Andric case Check::CheckNone:
1502d8e91e46SDimitry Andric return "invalid";
1503145449b1SDimitry Andric case Check::CheckMisspelled:
1504145449b1SDimitry Andric return "misspelled";
1505d8e91e46SDimitry Andric case Check::CheckPlain:
1506d8e91e46SDimitry Andric if (Count > 1)
1507b60736ecSDimitry Andric return WithModifiers("-COUNT");
1508b60736ecSDimitry Andric return WithModifiers("");
1509d8e91e46SDimitry Andric case Check::CheckNext:
1510b60736ecSDimitry Andric return WithModifiers("-NEXT");
1511d8e91e46SDimitry Andric case Check::CheckSame:
1512b60736ecSDimitry Andric return WithModifiers("-SAME");
1513d8e91e46SDimitry Andric case Check::CheckNot:
1514b60736ecSDimitry Andric return WithModifiers("-NOT");
1515d8e91e46SDimitry Andric case Check::CheckDAG:
1516b60736ecSDimitry Andric return WithModifiers("-DAG");
1517d8e91e46SDimitry Andric case Check::CheckLabel:
1518b60736ecSDimitry Andric return WithModifiers("-LABEL");
1519d8e91e46SDimitry Andric case Check::CheckEmpty:
1520b60736ecSDimitry Andric return WithModifiers("-EMPTY");
1521cfca06d7SDimitry Andric case Check::CheckComment:
1522cfca06d7SDimitry Andric return std::string(Prefix);
1523d8e91e46SDimitry Andric case Check::CheckEOF:
1524d8e91e46SDimitry Andric return "implicit EOF";
1525d8e91e46SDimitry Andric case Check::CheckBadNot:
1526d8e91e46SDimitry Andric return "bad NOT";
1527d8e91e46SDimitry Andric case Check::CheckBadCount:
1528d8e91e46SDimitry Andric return "bad COUNT";
1529d8e91e46SDimitry Andric }
1530d8e91e46SDimitry Andric llvm_unreachable("unknown FileCheckType");
1531d8e91e46SDimitry Andric }
1532d8e91e46SDimitry Andric
1533d8e91e46SDimitry Andric static std::pair<Check::FileCheckType, StringRef>
FindCheckType(const FileCheckRequest & Req,StringRef Buffer,StringRef Prefix,bool & Misspelled)1534145449b1SDimitry Andric FindCheckType(const FileCheckRequest &Req, StringRef Buffer, StringRef Prefix,
1535145449b1SDimitry Andric bool &Misspelled) {
1536d8e91e46SDimitry Andric if (Buffer.size() <= Prefix.size())
1537d8e91e46SDimitry Andric return {Check::CheckNone, StringRef()};
1538d8e91e46SDimitry Andric
1539b60736ecSDimitry Andric StringRef Rest = Buffer.drop_front(Prefix.size());
1540cfca06d7SDimitry Andric // Check for comment.
1541b60736ecSDimitry Andric if (llvm::is_contained(Req.CommentPrefixes, Prefix)) {
1542b60736ecSDimitry Andric if (Rest.consume_front(":"))
1543cfca06d7SDimitry Andric return {Check::CheckComment, Rest};
1544cfca06d7SDimitry Andric // Ignore a comment prefix if it has a suffix like "-NOT".
1545cfca06d7SDimitry Andric return {Check::CheckNone, StringRef()};
1546cfca06d7SDimitry Andric }
1547cfca06d7SDimitry Andric
1548b60736ecSDimitry Andric auto ConsumeModifiers = [&](Check::FileCheckType Ret)
1549b60736ecSDimitry Andric -> std::pair<Check::FileCheckType, StringRef> {
1550b60736ecSDimitry Andric if (Rest.consume_front(":"))
1551b60736ecSDimitry Andric return {Ret, Rest};
1552b60736ecSDimitry Andric if (!Rest.consume_front("{"))
1553b60736ecSDimitry Andric return {Check::CheckNone, StringRef()};
1554d8e91e46SDimitry Andric
1555b60736ecSDimitry Andric // Parse the modifiers, speparated by commas.
1556b60736ecSDimitry Andric do {
1557b60736ecSDimitry Andric // Allow whitespace in modifiers list.
1558b60736ecSDimitry Andric Rest = Rest.ltrim();
1559b60736ecSDimitry Andric if (Rest.consume_front("LITERAL"))
1560b60736ecSDimitry Andric Ret.setLiteralMatch();
1561b60736ecSDimitry Andric else
1562b60736ecSDimitry Andric return {Check::CheckNone, Rest};
1563b60736ecSDimitry Andric // Allow whitespace in modifiers list.
1564b60736ecSDimitry Andric Rest = Rest.ltrim();
1565b60736ecSDimitry Andric } while (Rest.consume_front(","));
1566b60736ecSDimitry Andric if (!Rest.consume_front("}:"))
1567b60736ecSDimitry Andric return {Check::CheckNone, Rest};
1568b60736ecSDimitry Andric return {Ret, Rest};
1569b60736ecSDimitry Andric };
1570b60736ecSDimitry Andric
1571b60736ecSDimitry Andric // Verify that the prefix is followed by directive modifiers or a colon.
1572b60736ecSDimitry Andric if (Rest.consume_front(":"))
1573b60736ecSDimitry Andric return {Check::CheckPlain, Rest};
1574b60736ecSDimitry Andric if (Rest.front() == '{')
1575b60736ecSDimitry Andric return ConsumeModifiers(Check::CheckPlain);
1576b60736ecSDimitry Andric
1577145449b1SDimitry Andric if (Rest.consume_front("_"))
1578145449b1SDimitry Andric Misspelled = true;
1579145449b1SDimitry Andric else if (!Rest.consume_front("-"))
1580d8e91e46SDimitry Andric return {Check::CheckNone, StringRef()};
1581d8e91e46SDimitry Andric
1582d8e91e46SDimitry Andric if (Rest.consume_front("COUNT-")) {
1583d8e91e46SDimitry Andric int64_t Count;
1584d8e91e46SDimitry Andric if (Rest.consumeInteger(10, Count))
1585d8e91e46SDimitry Andric // Error happened in parsing integer.
1586d8e91e46SDimitry Andric return {Check::CheckBadCount, Rest};
1587d8e91e46SDimitry Andric if (Count <= 0 || Count > INT32_MAX)
1588d8e91e46SDimitry Andric return {Check::CheckBadCount, Rest};
1589b60736ecSDimitry Andric if (Rest.front() != ':' && Rest.front() != '{')
1590d8e91e46SDimitry Andric return {Check::CheckBadCount, Rest};
1591b60736ecSDimitry Andric return ConsumeModifiers(
1592b60736ecSDimitry Andric Check::FileCheckType(Check::CheckPlain).setCount(Count));
1593d8e91e46SDimitry Andric }
1594d8e91e46SDimitry Andric
1595d8e91e46SDimitry Andric // You can't combine -NOT with another suffix.
1596312c0ed1SDimitry Andric if (Rest.starts_with("DAG-NOT:") || Rest.starts_with("NOT-DAG:") ||
1597312c0ed1SDimitry Andric Rest.starts_with("NEXT-NOT:") || Rest.starts_with("NOT-NEXT:") ||
1598312c0ed1SDimitry Andric Rest.starts_with("SAME-NOT:") || Rest.starts_with("NOT-SAME:") ||
1599312c0ed1SDimitry Andric Rest.starts_with("EMPTY-NOT:") || Rest.starts_with("NOT-EMPTY:"))
1600d8e91e46SDimitry Andric return {Check::CheckBadNot, Rest};
1601d8e91e46SDimitry Andric
1602b60736ecSDimitry Andric if (Rest.consume_front("NEXT"))
1603b60736ecSDimitry Andric return ConsumeModifiers(Check::CheckNext);
1604b60736ecSDimitry Andric
1605b60736ecSDimitry Andric if (Rest.consume_front("SAME"))
1606b60736ecSDimitry Andric return ConsumeModifiers(Check::CheckSame);
1607b60736ecSDimitry Andric
1608b60736ecSDimitry Andric if (Rest.consume_front("NOT"))
1609b60736ecSDimitry Andric return ConsumeModifiers(Check::CheckNot);
1610b60736ecSDimitry Andric
1611b60736ecSDimitry Andric if (Rest.consume_front("DAG"))
1612b60736ecSDimitry Andric return ConsumeModifiers(Check::CheckDAG);
1613b60736ecSDimitry Andric
1614b60736ecSDimitry Andric if (Rest.consume_front("LABEL"))
1615b60736ecSDimitry Andric return ConsumeModifiers(Check::CheckLabel);
1616b60736ecSDimitry Andric
1617b60736ecSDimitry Andric if (Rest.consume_front("EMPTY"))
1618b60736ecSDimitry Andric return ConsumeModifiers(Check::CheckEmpty);
1619b60736ecSDimitry Andric
1620d8e91e46SDimitry Andric return {Check::CheckNone, Rest};
1621d8e91e46SDimitry Andric }
1622d8e91e46SDimitry Andric
1623145449b1SDimitry Andric static std::pair<Check::FileCheckType, StringRef>
FindCheckType(const FileCheckRequest & Req,StringRef Buffer,StringRef Prefix)1624145449b1SDimitry Andric FindCheckType(const FileCheckRequest &Req, StringRef Buffer, StringRef Prefix) {
1625145449b1SDimitry Andric bool Misspelled = false;
1626145449b1SDimitry Andric auto Res = FindCheckType(Req, Buffer, Prefix, Misspelled);
1627145449b1SDimitry Andric if (Res.first != Check::CheckNone && Misspelled)
1628145449b1SDimitry Andric return {Check::CheckMisspelled, Res.second};
1629145449b1SDimitry Andric return Res;
1630145449b1SDimitry Andric }
1631145449b1SDimitry Andric
1632d8e91e46SDimitry Andric // From the given position, find the next character after the word.
SkipWord(StringRef Str,size_t Loc)1633d8e91e46SDimitry Andric static size_t SkipWord(StringRef Str, size_t Loc) {
1634d8e91e46SDimitry Andric while (Loc < Str.size() && IsPartOfWord(Str[Loc]))
1635d8e91e46SDimitry Andric ++Loc;
1636d8e91e46SDimitry Andric return Loc;
1637d8e91e46SDimitry Andric }
1638d8e91e46SDimitry Andric
1639b1c73532SDimitry Andric static const char *DefaultCheckPrefixes[] = {"CHECK"};
1640b1c73532SDimitry Andric static const char *DefaultCommentPrefixes[] = {"COM", "RUN"};
1641b1c73532SDimitry Andric
addDefaultPrefixes(FileCheckRequest & Req)1642b1c73532SDimitry Andric static void addDefaultPrefixes(FileCheckRequest &Req) {
1643b1c73532SDimitry Andric if (Req.CheckPrefixes.empty()) {
1644b1c73532SDimitry Andric for (const char *Prefix : DefaultCheckPrefixes)
1645b1c73532SDimitry Andric Req.CheckPrefixes.push_back(Prefix);
1646b1c73532SDimitry Andric Req.IsDefaultCheckPrefix = true;
1647b1c73532SDimitry Andric }
1648b1c73532SDimitry Andric if (Req.CommentPrefixes.empty())
1649b1c73532SDimitry Andric for (const char *Prefix : DefaultCommentPrefixes)
1650b1c73532SDimitry Andric Req.CommentPrefixes.push_back(Prefix);
1651b1c73532SDimitry Andric }
1652b1c73532SDimitry Andric
1653b1c73532SDimitry Andric struct PrefixMatcher {
1654b1c73532SDimitry Andric /// Prefixes and their first occurrence past the current position.
1655b1c73532SDimitry Andric SmallVector<std::pair<StringRef, size_t>> Prefixes;
1656b1c73532SDimitry Andric StringRef Input;
1657b1c73532SDimitry Andric
PrefixMatcherPrefixMatcher1658b1c73532SDimitry Andric PrefixMatcher(ArrayRef<StringRef> CheckPrefixes,
1659b1c73532SDimitry Andric ArrayRef<StringRef> CommentPrefixes, StringRef Input)
1660b1c73532SDimitry Andric : Input(Input) {
1661b1c73532SDimitry Andric for (StringRef Prefix : CheckPrefixes)
1662b1c73532SDimitry Andric Prefixes.push_back({Prefix, Input.find(Prefix)});
1663b1c73532SDimitry Andric for (StringRef Prefix : CommentPrefixes)
1664b1c73532SDimitry Andric Prefixes.push_back({Prefix, Input.find(Prefix)});
1665b1c73532SDimitry Andric
1666b1c73532SDimitry Andric // Sort by descending length.
1667b1c73532SDimitry Andric llvm::sort(Prefixes,
1668b1c73532SDimitry Andric [](auto A, auto B) { return A.first.size() > B.first.size(); });
1669b1c73532SDimitry Andric }
1670b1c73532SDimitry Andric
1671b1c73532SDimitry Andric /// Find the next match of a prefix in Buffer.
1672b1c73532SDimitry Andric /// Returns empty StringRef if not found.
matchPrefixMatcher1673b1c73532SDimitry Andric StringRef match(StringRef Buffer) {
1674b1c73532SDimitry Andric assert(Buffer.data() >= Input.data() &&
1675b1c73532SDimitry Andric Buffer.data() + Buffer.size() == Input.data() + Input.size() &&
1676b1c73532SDimitry Andric "Buffer must be suffix of Input");
1677b1c73532SDimitry Andric
1678b1c73532SDimitry Andric size_t From = Buffer.data() - Input.data();
1679b1c73532SDimitry Andric StringRef Match;
1680b1c73532SDimitry Andric for (auto &[Prefix, Pos] : Prefixes) {
1681b1c73532SDimitry Andric // If the last occurrence was before From, find the next one after From.
1682b1c73532SDimitry Andric if (Pos < From)
1683b1c73532SDimitry Andric Pos = Input.find(Prefix, From);
1684b1c73532SDimitry Andric // Find the first prefix with the lowest position.
1685b1c73532SDimitry Andric if (Pos != StringRef::npos &&
1686b1c73532SDimitry Andric (Match.empty() || size_t(Match.data() - Input.data()) > Pos))
1687b1c73532SDimitry Andric Match = StringRef(Input.substr(Pos, Prefix.size()));
1688b1c73532SDimitry Andric }
1689b1c73532SDimitry Andric return Match;
1690b1c73532SDimitry Andric }
1691b1c73532SDimitry Andric };
1692b1c73532SDimitry Andric
1693e6d15924SDimitry Andric /// Searches the buffer for the first prefix in the prefix regular expression.
1694d8e91e46SDimitry Andric ///
1695d8e91e46SDimitry Andric /// This searches the buffer using the provided regular expression, however it
1696d8e91e46SDimitry Andric /// enforces constraints beyond that:
1697d8e91e46SDimitry Andric /// 1) The found prefix must not be a suffix of something that looks like
1698d8e91e46SDimitry Andric /// a valid prefix.
1699d8e91e46SDimitry Andric /// 2) The found prefix must be followed by a valid check type suffix using \c
1700d8e91e46SDimitry Andric /// FindCheckType above.
1701d8e91e46SDimitry Andric ///
1702e6d15924SDimitry Andric /// \returns a pair of StringRefs into the Buffer, which combines:
1703d8e91e46SDimitry Andric /// - the first match of the regular expression to satisfy these two is
1704d8e91e46SDimitry Andric /// returned,
1705d8e91e46SDimitry Andric /// otherwise an empty StringRef is returned to indicate failure.
1706d8e91e46SDimitry Andric /// - buffer rewound to the location right after parsed suffix, for parsing
1707d8e91e46SDimitry Andric /// to continue from
1708d8e91e46SDimitry Andric ///
1709d8e91e46SDimitry Andric /// If this routine returns a valid prefix, it will also shrink \p Buffer to
1710d8e91e46SDimitry Andric /// start at the beginning of the returned prefix, increment \p LineNumber for
1711d8e91e46SDimitry Andric /// each new line consumed from \p Buffer, and set \p CheckTy to the type of
1712d8e91e46SDimitry Andric /// check found by examining the suffix.
1713d8e91e46SDimitry Andric ///
1714d8e91e46SDimitry Andric /// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy
1715d8e91e46SDimitry Andric /// is unspecified.
1716d8e91e46SDimitry Andric static std::pair<StringRef, StringRef>
FindFirstMatchingPrefix(const FileCheckRequest & Req,PrefixMatcher & Matcher,StringRef & Buffer,unsigned & LineNumber,Check::FileCheckType & CheckTy)1717b1c73532SDimitry Andric FindFirstMatchingPrefix(const FileCheckRequest &Req, PrefixMatcher &Matcher,
1718cfca06d7SDimitry Andric StringRef &Buffer, unsigned &LineNumber,
1719cfca06d7SDimitry Andric Check::FileCheckType &CheckTy) {
1720d8e91e46SDimitry Andric while (!Buffer.empty()) {
1721b1c73532SDimitry Andric // Find the first (longest) prefix match.
1722b1c73532SDimitry Andric StringRef Prefix = Matcher.match(Buffer);
1723b1c73532SDimitry Andric if (Prefix.empty())
1724d8e91e46SDimitry Andric // No match at all, bail.
1725d8e91e46SDimitry Andric return {StringRef(), StringRef()};
1726d8e91e46SDimitry Andric
1727d8e91e46SDimitry Andric assert(Prefix.data() >= Buffer.data() &&
1728d8e91e46SDimitry Andric Prefix.data() < Buffer.data() + Buffer.size() &&
1729d8e91e46SDimitry Andric "Prefix doesn't start inside of buffer!");
1730d8e91e46SDimitry Andric size_t Loc = Prefix.data() - Buffer.data();
1731d8e91e46SDimitry Andric StringRef Skipped = Buffer.substr(0, Loc);
1732d8e91e46SDimitry Andric Buffer = Buffer.drop_front(Loc);
1733d8e91e46SDimitry Andric LineNumber += Skipped.count('\n');
1734d8e91e46SDimitry Andric
1735d8e91e46SDimitry Andric // Check that the matched prefix isn't a suffix of some other check-like
1736d8e91e46SDimitry Andric // word.
1737d8e91e46SDimitry Andric // FIXME: This is a very ad-hoc check. it would be better handled in some
1738d8e91e46SDimitry Andric // other way. Among other things it seems hard to distinguish between
1739d8e91e46SDimitry Andric // intentional and unintentional uses of this feature.
1740d8e91e46SDimitry Andric if (Skipped.empty() || !IsPartOfWord(Skipped.back())) {
1741d8e91e46SDimitry Andric // Now extract the type.
1742d8e91e46SDimitry Andric StringRef AfterSuffix;
1743cfca06d7SDimitry Andric std::tie(CheckTy, AfterSuffix) = FindCheckType(Req, Buffer, Prefix);
1744d8e91e46SDimitry Andric
1745d8e91e46SDimitry Andric // If we've found a valid check type for this prefix, we're done.
1746d8e91e46SDimitry Andric if (CheckTy != Check::CheckNone)
1747d8e91e46SDimitry Andric return {Prefix, AfterSuffix};
1748d8e91e46SDimitry Andric }
1749d8e91e46SDimitry Andric
1750d8e91e46SDimitry Andric // If we didn't successfully find a prefix, we need to skip this invalid
1751d8e91e46SDimitry Andric // prefix and continue scanning. We directly skip the prefix that was
1752d8e91e46SDimitry Andric // matched and any additional parts of that check-like word.
1753d8e91e46SDimitry Andric Buffer = Buffer.drop_front(SkipWord(Buffer, Prefix.size()));
1754d8e91e46SDimitry Andric }
1755d8e91e46SDimitry Andric
1756d8e91e46SDimitry Andric // We ran out of buffer while skipping partial matches so give up.
1757d8e91e46SDimitry Andric return {StringRef(), StringRef()};
1758d8e91e46SDimitry Andric }
1759d8e91e46SDimitry Andric
createLineVariable()1760e6d15924SDimitry Andric void FileCheckPatternContext::createLineVariable() {
1761e6d15924SDimitry Andric assert(!LineVariable && "@LINE pseudo numeric variable already created");
1762e6d15924SDimitry Andric StringRef LineName = "@LINE";
1763cfca06d7SDimitry Andric LineVariable = makeNumericVariable(
1764cfca06d7SDimitry Andric LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
1765e6d15924SDimitry Andric GlobalNumericVariableTable[LineName] = LineVariable;
1766e6d15924SDimitry Andric }
1767e6d15924SDimitry Andric
FileCheck(FileCheckRequest Req)17681d5ae102SDimitry Andric FileCheck::FileCheck(FileCheckRequest Req)
17691d5ae102SDimitry Andric : Req(Req), PatternContext(std::make_unique<FileCheckPatternContext>()),
17701d5ae102SDimitry Andric CheckStrings(std::make_unique<std::vector<FileCheckString>>()) {}
17711d5ae102SDimitry Andric
17721d5ae102SDimitry Andric FileCheck::~FileCheck() = default;
17731d5ae102SDimitry Andric
readCheckFile(SourceMgr & SM,StringRef Buffer,std::pair<unsigned,unsigned> * ImpPatBufferIDRange)1774cfca06d7SDimitry Andric bool FileCheck::readCheckFile(
1775b1c73532SDimitry Andric SourceMgr &SM, StringRef Buffer,
1776cfca06d7SDimitry Andric std::pair<unsigned, unsigned> *ImpPatBufferIDRange) {
1777cfca06d7SDimitry Andric if (ImpPatBufferIDRange)
1778cfca06d7SDimitry Andric ImpPatBufferIDRange->first = ImpPatBufferIDRange->second = 0;
1779cfca06d7SDimitry Andric
1780e6d15924SDimitry Andric Error DefineError =
17811d5ae102SDimitry Andric PatternContext->defineCmdlineVariables(Req.GlobalDefines, SM);
1782e6d15924SDimitry Andric if (DefineError) {
1783e6d15924SDimitry Andric logAllUnhandledErrors(std::move(DefineError), errs());
1784e6d15924SDimitry Andric return true;
1785e6d15924SDimitry Andric }
1786e6d15924SDimitry Andric
17871d5ae102SDimitry Andric PatternContext->createLineVariable();
1788e6d15924SDimitry Andric
17894df029ccSDimitry Andric std::vector<FileCheckString::DagNotPrefixInfo> ImplicitNegativeChecks;
1790cfca06d7SDimitry Andric for (StringRef PatternString : Req.ImplicitCheckNot) {
1791d8e91e46SDimitry Andric // Create a buffer with fake command line content in order to display the
1792d8e91e46SDimitry Andric // command line option responsible for the specific implicit CHECK-NOT.
1793d8e91e46SDimitry Andric std::string Prefix = "-implicit-check-not='";
1794d8e91e46SDimitry Andric std::string Suffix = "'";
1795d8e91e46SDimitry Andric std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy(
1796cfca06d7SDimitry Andric (Prefix + PatternString + Suffix).str(), "command line");
1797d8e91e46SDimitry Andric
1798d8e91e46SDimitry Andric StringRef PatternInBuffer =
1799d8e91e46SDimitry Andric CmdLine->getBuffer().substr(Prefix.size(), PatternString.size());
1800cfca06d7SDimitry Andric unsigned BufferID = SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
1801cfca06d7SDimitry Andric if (ImpPatBufferIDRange) {
1802cfca06d7SDimitry Andric if (ImpPatBufferIDRange->first == ImpPatBufferIDRange->second) {
1803cfca06d7SDimitry Andric ImpPatBufferIDRange->first = BufferID;
1804cfca06d7SDimitry Andric ImpPatBufferIDRange->second = BufferID + 1;
1805cfca06d7SDimitry Andric } else {
1806cfca06d7SDimitry Andric assert(BufferID == ImpPatBufferIDRange->second &&
1807cfca06d7SDimitry Andric "expected consecutive source buffer IDs");
1808cfca06d7SDimitry Andric ++ImpPatBufferIDRange->second;
1809cfca06d7SDimitry Andric }
1810cfca06d7SDimitry Andric }
1811d8e91e46SDimitry Andric
18124df029ccSDimitry Andric ImplicitNegativeChecks.emplace_back(
18134df029ccSDimitry Andric Pattern(Check::CheckNot, PatternContext.get()),
18144df029ccSDimitry Andric StringRef("IMPLICIT-CHECK"));
18154df029ccSDimitry Andric ImplicitNegativeChecks.back().DagNotPat.parsePattern(
18164df029ccSDimitry Andric PatternInBuffer, "IMPLICIT-CHECK", SM, Req);
1817d8e91e46SDimitry Andric }
1818d8e91e46SDimitry Andric
18194df029ccSDimitry Andric std::vector<FileCheckString::DagNotPrefixInfo> DagNotMatches =
18204df029ccSDimitry Andric ImplicitNegativeChecks;
1821d8e91e46SDimitry Andric // LineNumber keeps track of the line on which CheckPrefix instances are
1822d8e91e46SDimitry Andric // found.
1823d8e91e46SDimitry Andric unsigned LineNumber = 1;
1824d8e91e46SDimitry Andric
1825b1c73532SDimitry Andric addDefaultPrefixes(Req);
1826b1c73532SDimitry Andric PrefixMatcher Matcher(Req.CheckPrefixes, Req.CommentPrefixes, Buffer);
1827b60736ecSDimitry Andric std::set<StringRef> PrefixesNotFound(Req.CheckPrefixes.begin(),
1828b60736ecSDimitry Andric Req.CheckPrefixes.end());
1829b60736ecSDimitry Andric const size_t DistinctPrefixes = PrefixesNotFound.size();
1830b60736ecSDimitry Andric while (true) {
1831d8e91e46SDimitry Andric Check::FileCheckType CheckTy;
1832d8e91e46SDimitry Andric
1833d8e91e46SDimitry Andric // See if a prefix occurs in the memory buffer.
1834d8e91e46SDimitry Andric StringRef UsedPrefix;
1835d8e91e46SDimitry Andric StringRef AfterSuffix;
1836d8e91e46SDimitry Andric std::tie(UsedPrefix, AfterSuffix) =
1837b1c73532SDimitry Andric FindFirstMatchingPrefix(Req, Matcher, Buffer, LineNumber, CheckTy);
1838d8e91e46SDimitry Andric if (UsedPrefix.empty())
1839d8e91e46SDimitry Andric break;
1840cfca06d7SDimitry Andric if (CheckTy != Check::CheckComment)
1841b60736ecSDimitry Andric PrefixesNotFound.erase(UsedPrefix);
1842cfca06d7SDimitry Andric
1843d8e91e46SDimitry Andric assert(UsedPrefix.data() == Buffer.data() &&
1844d8e91e46SDimitry Andric "Failed to move Buffer's start forward, or pointed prefix outside "
1845d8e91e46SDimitry Andric "of the buffer!");
1846d8e91e46SDimitry Andric assert(AfterSuffix.data() >= Buffer.data() &&
1847d8e91e46SDimitry Andric AfterSuffix.data() < Buffer.data() + Buffer.size() &&
1848d8e91e46SDimitry Andric "Parsing after suffix doesn't start inside of buffer!");
1849d8e91e46SDimitry Andric
1850d8e91e46SDimitry Andric // Location to use for error messages.
1851d8e91e46SDimitry Andric const char *UsedPrefixStart = UsedPrefix.data();
1852d8e91e46SDimitry Andric
1853d8e91e46SDimitry Andric // Skip the buffer to the end of parsed suffix (or just prefix, if no good
1854d8e91e46SDimitry Andric // suffix was processed).
1855d8e91e46SDimitry Andric Buffer = AfterSuffix.empty() ? Buffer.drop_front(UsedPrefix.size())
1856d8e91e46SDimitry Andric : AfterSuffix;
1857d8e91e46SDimitry Andric
1858145449b1SDimitry Andric // Complain about misspelled directives.
1859145449b1SDimitry Andric if (CheckTy == Check::CheckMisspelled) {
1860145449b1SDimitry Andric StringRef UsedDirective(UsedPrefix.data(),
1861145449b1SDimitry Andric AfterSuffix.data() - UsedPrefix.data());
1862145449b1SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(UsedDirective.data()),
1863145449b1SDimitry Andric SourceMgr::DK_Error,
1864145449b1SDimitry Andric "misspelled directive '" + UsedDirective + "'");
1865145449b1SDimitry Andric return true;
1866145449b1SDimitry Andric }
1867145449b1SDimitry Andric
1868d8e91e46SDimitry Andric // Complain about useful-looking but unsupported suffixes.
1869d8e91e46SDimitry Andric if (CheckTy == Check::CheckBadNot) {
1870d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,
1871d8e91e46SDimitry Andric "unsupported -NOT combo on prefix '" + UsedPrefix + "'");
1872d8e91e46SDimitry Andric return true;
1873d8e91e46SDimitry Andric }
1874d8e91e46SDimitry Andric
1875d8e91e46SDimitry Andric // Complain about invalid count specification.
1876d8e91e46SDimitry Andric if (CheckTy == Check::CheckBadCount) {
1877d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,
1878d8e91e46SDimitry Andric "invalid count in -COUNT specification on prefix '" +
1879d8e91e46SDimitry Andric UsedPrefix + "'");
1880d8e91e46SDimitry Andric return true;
1881d8e91e46SDimitry Andric }
1882d8e91e46SDimitry Andric
1883d8e91e46SDimitry Andric // Okay, we found the prefix, yay. Remember the rest of the line, but ignore
1884d8e91e46SDimitry Andric // leading whitespace.
1885d8e91e46SDimitry Andric if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
1886d8e91e46SDimitry Andric Buffer = Buffer.substr(Buffer.find_first_not_of(" \t"));
1887d8e91e46SDimitry Andric
1888d8e91e46SDimitry Andric // Scan ahead to the end of line.
1889d8e91e46SDimitry Andric size_t EOL = Buffer.find_first_of("\n\r");
1890d8e91e46SDimitry Andric
1891d8e91e46SDimitry Andric // Remember the location of the start of the pattern, for diagnostics.
1892d8e91e46SDimitry Andric SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
1893d8e91e46SDimitry Andric
1894cfca06d7SDimitry Andric // Extract the pattern from the buffer.
1895cfca06d7SDimitry Andric StringRef PatternBuffer = Buffer.substr(0, EOL);
1896cfca06d7SDimitry Andric Buffer = Buffer.substr(EOL);
1897cfca06d7SDimitry Andric
1898cfca06d7SDimitry Andric // If this is a comment, we're done.
1899cfca06d7SDimitry Andric if (CheckTy == Check::CheckComment)
1900cfca06d7SDimitry Andric continue;
1901cfca06d7SDimitry Andric
1902d8e91e46SDimitry Andric // Parse the pattern.
1903706b4fc4SDimitry Andric Pattern P(CheckTy, PatternContext.get(), LineNumber);
1904cfca06d7SDimitry Andric if (P.parsePattern(PatternBuffer, UsedPrefix, SM, Req))
1905d8e91e46SDimitry Andric return true;
1906d8e91e46SDimitry Andric
1907d8e91e46SDimitry Andric // Verify that CHECK-LABEL lines do not define or use variables
1908d8e91e46SDimitry Andric if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {
1909d8e91e46SDimitry Andric SM.PrintMessage(
1910d8e91e46SDimitry Andric SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error,
1911d8e91e46SDimitry Andric "found '" + UsedPrefix + "-LABEL:'"
1912d8e91e46SDimitry Andric " with variable definition or use");
1913d8e91e46SDimitry Andric return true;
1914d8e91e46SDimitry Andric }
1915d8e91e46SDimitry Andric
1916d8e91e46SDimitry Andric // Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them.
1917d8e91e46SDimitry Andric if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame ||
1918d8e91e46SDimitry Andric CheckTy == Check::CheckEmpty) &&
19191d5ae102SDimitry Andric CheckStrings->empty()) {
1920d8e91e46SDimitry Andric StringRef Type = CheckTy == Check::CheckNext
1921d8e91e46SDimitry Andric ? "NEXT"
1922d8e91e46SDimitry Andric : CheckTy == Check::CheckEmpty ? "EMPTY" : "SAME";
1923d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
1924d8e91e46SDimitry Andric SourceMgr::DK_Error,
1925d8e91e46SDimitry Andric "found '" + UsedPrefix + "-" + Type +
1926d8e91e46SDimitry Andric "' without previous '" + UsedPrefix + ": line");
1927d8e91e46SDimitry Andric return true;
1928d8e91e46SDimitry Andric }
1929d8e91e46SDimitry Andric
1930d8e91e46SDimitry Andric // Handle CHECK-DAG/-NOT.
1931d8e91e46SDimitry Andric if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) {
19324df029ccSDimitry Andric DagNotMatches.emplace_back(P, UsedPrefix);
1933d8e91e46SDimitry Andric continue;
1934d8e91e46SDimitry Andric }
1935d8e91e46SDimitry Andric
1936d8e91e46SDimitry Andric // Okay, add the string we captured to the output vector and move on.
19371d5ae102SDimitry Andric CheckStrings->emplace_back(P, UsedPrefix, PatternLoc);
19381d5ae102SDimitry Andric std::swap(DagNotMatches, CheckStrings->back().DagNotStrings);
1939d8e91e46SDimitry Andric DagNotMatches = ImplicitNegativeChecks;
1940d8e91e46SDimitry Andric }
1941d8e91e46SDimitry Andric
1942cfca06d7SDimitry Andric // When there are no used prefixes we report an error except in the case that
1943cfca06d7SDimitry Andric // no prefix is specified explicitly but -implicit-check-not is specified.
1944b60736ecSDimitry Andric const bool NoPrefixesFound = PrefixesNotFound.size() == DistinctPrefixes;
1945b60736ecSDimitry Andric const bool SomePrefixesUnexpectedlyNotUsed =
1946b60736ecSDimitry Andric !Req.AllowUnusedPrefixes && !PrefixesNotFound.empty();
1947b60736ecSDimitry Andric if ((NoPrefixesFound || SomePrefixesUnexpectedlyNotUsed) &&
1948cfca06d7SDimitry Andric (ImplicitNegativeChecks.empty() || !Req.IsDefaultCheckPrefix)) {
1949cfca06d7SDimitry Andric errs() << "error: no check strings found with prefix"
1950b60736ecSDimitry Andric << (PrefixesNotFound.size() > 1 ? "es " : " ");
1951b60736ecSDimitry Andric bool First = true;
1952b60736ecSDimitry Andric for (StringRef MissingPrefix : PrefixesNotFound) {
1953b60736ecSDimitry Andric if (!First)
1954cfca06d7SDimitry Andric errs() << ", ";
1955b60736ecSDimitry Andric errs() << "\'" << MissingPrefix << ":'";
1956b60736ecSDimitry Andric First = false;
1957cfca06d7SDimitry Andric }
1958cfca06d7SDimitry Andric errs() << '\n';
1959cfca06d7SDimitry Andric return true;
1960cfca06d7SDimitry Andric }
1961cfca06d7SDimitry Andric
1962cfca06d7SDimitry Andric // Add an EOF pattern for any trailing --implicit-check-not/CHECK-DAG/-NOTs,
1963cfca06d7SDimitry Andric // and use the first prefix as a filler for the error message.
1964d8e91e46SDimitry Andric if (!DagNotMatches.empty()) {
19651d5ae102SDimitry Andric CheckStrings->emplace_back(
1966706b4fc4SDimitry Andric Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1),
1967e6d15924SDimitry Andric *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
19681d5ae102SDimitry Andric std::swap(DagNotMatches, CheckStrings->back().DagNotStrings);
1969d8e91e46SDimitry Andric }
1970d8e91e46SDimitry Andric
1971d8e91e46SDimitry Andric return false;
1972d8e91e46SDimitry Andric }
1973d8e91e46SDimitry Andric
1974344a3780SDimitry Andric /// Returns either (1) \c ErrorSuccess if there was no error or (2)
1975344a3780SDimitry Andric /// \c ErrorReported if an error was reported, such as an unexpected match.
printMatch(bool ExpectedMatch,const SourceMgr & SM,StringRef Prefix,SMLoc Loc,const Pattern & Pat,int MatchedCount,StringRef Buffer,Pattern::MatchResult MatchResult,const FileCheckRequest & Req,std::vector<FileCheckDiag> * Diags)1976344a3780SDimitry Andric static Error printMatch(bool ExpectedMatch, const SourceMgr &SM,
1977706b4fc4SDimitry Andric StringRef Prefix, SMLoc Loc, const Pattern &Pat,
1978344a3780SDimitry Andric int MatchedCount, StringRef Buffer,
1979344a3780SDimitry Andric Pattern::MatchResult MatchResult,
1980344a3780SDimitry Andric const FileCheckRequest &Req,
1981d8e91e46SDimitry Andric std::vector<FileCheckDiag> *Diags) {
1982344a3780SDimitry Andric // Suppress some verbosity if there's no error.
1983344a3780SDimitry Andric bool HasError = !ExpectedMatch || MatchResult.TheError;
1984e6d15924SDimitry Andric bool PrintDiag = true;
1985344a3780SDimitry Andric if (!HasError) {
1986d8e91e46SDimitry Andric if (!Req.Verbose)
1987344a3780SDimitry Andric return ErrorReported::reportedOrSuccess(HasError);
1988d8e91e46SDimitry Andric if (!Req.VerboseVerbose && Pat.getCheckTy() == Check::CheckEOF)
1989344a3780SDimitry Andric return ErrorReported::reportedOrSuccess(HasError);
1990e6d15924SDimitry Andric // Due to their verbosity, we don't print verbose diagnostics here if we're
1991344a3780SDimitry Andric // gathering them for Diags to be rendered elsewhere, but we always print
1992344a3780SDimitry Andric // other diagnostics.
1993e6d15924SDimitry Andric PrintDiag = !Diags;
1994d8e91e46SDimitry Andric }
1995344a3780SDimitry Andric
1996344a3780SDimitry Andric // Add "found" diagnostic, substitutions, and variable definitions to Diags.
1997b60736ecSDimitry Andric FileCheckDiag::MatchType MatchTy = ExpectedMatch
1998b60736ecSDimitry Andric ? FileCheckDiag::MatchFoundAndExpected
1999b60736ecSDimitry Andric : FileCheckDiag::MatchFoundButExcluded;
2000b60736ecSDimitry Andric SMRange MatchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(),
2001344a3780SDimitry Andric Buffer, MatchResult.TheMatch->Pos,
2002344a3780SDimitry Andric MatchResult.TheMatch->Len, Diags);
2003b60736ecSDimitry Andric if (Diags) {
2004b60736ecSDimitry Andric Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, Diags);
2005b60736ecSDimitry Andric Pat.printVariableDefs(SM, MatchTy, Diags);
2006b60736ecSDimitry Andric }
2007344a3780SDimitry Andric if (!PrintDiag) {
2008344a3780SDimitry Andric assert(!HasError && "expected to report more diagnostics for error");
2009344a3780SDimitry Andric return ErrorReported::reportedOrSuccess(HasError);
2010344a3780SDimitry Andric }
2011e6d15924SDimitry Andric
2012344a3780SDimitry Andric // Print the match.
2013d8e91e46SDimitry Andric std::string Message = formatv("{0}: {1} string found in input",
2014d8e91e46SDimitry Andric Pat.getCheckTy().getDescription(Prefix),
2015d8e91e46SDimitry Andric (ExpectedMatch ? "expected" : "excluded"))
2016d8e91e46SDimitry Andric .str();
2017d8e91e46SDimitry Andric if (Pat.getCount() > 1)
2018d8e91e46SDimitry Andric Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
2019d8e91e46SDimitry Andric SM.PrintMessage(
2020d8e91e46SDimitry Andric Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);
2021d8e91e46SDimitry Andric SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",
2022d8e91e46SDimitry Andric {MatchRange});
2023344a3780SDimitry Andric
2024344a3780SDimitry Andric // Print additional information, which can be useful even if there are errors.
2025b60736ecSDimitry Andric Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, nullptr);
2026b60736ecSDimitry Andric Pat.printVariableDefs(SM, MatchTy, nullptr);
2027344a3780SDimitry Andric
2028344a3780SDimitry Andric // Print errors and add them to Diags. We report these errors after the match
2029344a3780SDimitry Andric // itself because we found them after the match. If we had found them before
2030344a3780SDimitry Andric // the match, we'd be in printNoMatch.
2031344a3780SDimitry Andric handleAllErrors(std::move(MatchResult.TheError),
2032344a3780SDimitry Andric [&](const ErrorDiagnostic &E) {
2033344a3780SDimitry Andric E.log(errs());
2034344a3780SDimitry Andric if (Diags) {
2035344a3780SDimitry Andric Diags->emplace_back(SM, Pat.getCheckTy(), Loc,
2036344a3780SDimitry Andric FileCheckDiag::MatchFoundErrorNote,
2037344a3780SDimitry Andric E.getRange(), E.getMessage().str());
2038344a3780SDimitry Andric }
2039344a3780SDimitry Andric });
2040344a3780SDimitry Andric return ErrorReported::reportedOrSuccess(HasError);
2041d8e91e46SDimitry Andric }
2042d8e91e46SDimitry Andric
2043344a3780SDimitry Andric /// Returns either (1) \c ErrorSuccess if there was no error, or (2)
2044344a3780SDimitry Andric /// \c ErrorReported if an error was reported, such as an expected match not
2045344a3780SDimitry Andric /// found.
printNoMatch(bool ExpectedMatch,const SourceMgr & SM,StringRef Prefix,SMLoc Loc,const Pattern & Pat,int MatchedCount,StringRef Buffer,Error MatchError,bool VerboseVerbose,std::vector<FileCheckDiag> * Diags)2046344a3780SDimitry Andric static Error printNoMatch(bool ExpectedMatch, const SourceMgr &SM,
2047706b4fc4SDimitry Andric StringRef Prefix, SMLoc Loc, const Pattern &Pat,
2048344a3780SDimitry Andric int MatchedCount, StringRef Buffer, Error MatchError,
2049344a3780SDimitry Andric bool VerboseVerbose,
2050344a3780SDimitry Andric std::vector<FileCheckDiag> *Diags) {
2051344a3780SDimitry Andric // Print any pattern errors, and record them to be added to Diags later.
2052344a3780SDimitry Andric bool HasError = ExpectedMatch;
2053344a3780SDimitry Andric bool HasPatternError = false;
2054b60736ecSDimitry Andric FileCheckDiag::MatchType MatchTy = ExpectedMatch
2055b60736ecSDimitry Andric ? FileCheckDiag::MatchNoneButExpected
2056b60736ecSDimitry Andric : FileCheckDiag::MatchNoneAndExcluded;
2057344a3780SDimitry Andric SmallVector<std::string, 4> ErrorMsgs;
2058344a3780SDimitry Andric handleAllErrors(
2059344a3780SDimitry Andric std::move(MatchError),
2060344a3780SDimitry Andric [&](const ErrorDiagnostic &E) {
2061344a3780SDimitry Andric HasError = HasPatternError = true;
2062344a3780SDimitry Andric MatchTy = FileCheckDiag::MatchNoneForInvalidPattern;
2063344a3780SDimitry Andric E.log(errs());
2064b60736ecSDimitry Andric if (Diags)
2065344a3780SDimitry Andric ErrorMsgs.push_back(E.getMessage().str());
2066344a3780SDimitry Andric },
2067344a3780SDimitry Andric // NotFoundError is why printNoMatch was invoked.
2068344a3780SDimitry Andric [](const NotFoundError &E) {});
2069344a3780SDimitry Andric
2070344a3780SDimitry Andric // Suppress some verbosity if there's no error.
2071344a3780SDimitry Andric bool PrintDiag = true;
2072344a3780SDimitry Andric if (!HasError) {
2073344a3780SDimitry Andric if (!VerboseVerbose)
2074344a3780SDimitry Andric return ErrorReported::reportedOrSuccess(HasError);
2075344a3780SDimitry Andric // Due to their verbosity, we don't print verbose diagnostics here if we're
2076344a3780SDimitry Andric // gathering them for Diags to be rendered elsewhere, but we always print
2077344a3780SDimitry Andric // other diagnostics.
2078344a3780SDimitry Andric PrintDiag = !Diags;
2079e6d15924SDimitry Andric }
2080e6d15924SDimitry Andric
2081344a3780SDimitry Andric // Add "not found" diagnostic, substitutions, and pattern errors to Diags.
2082344a3780SDimitry Andric //
2083344a3780SDimitry Andric // We handle Diags a little differently than the errors we print directly:
2084344a3780SDimitry Andric // we add the "not found" diagnostic to Diags even if there are pattern
2085344a3780SDimitry Andric // errors. The reason is that we need to attach pattern errors as notes
2086344a3780SDimitry Andric // somewhere in the input, and the input search range from the "not found"
2087344a3780SDimitry Andric // diagnostic is all we have to anchor them.
2088344a3780SDimitry Andric SMRange SearchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(),
2089344a3780SDimitry Andric Buffer, 0, Buffer.size(), Diags);
2090344a3780SDimitry Andric if (Diags) {
2091344a3780SDimitry Andric SMRange NoteRange = SMRange(SearchRange.Start, SearchRange.Start);
2092344a3780SDimitry Andric for (StringRef ErrorMsg : ErrorMsgs)
2093344a3780SDimitry Andric Diags->emplace_back(SM, Pat.getCheckTy(), Loc, MatchTy, NoteRange,
2094344a3780SDimitry Andric ErrorMsg);
2095344a3780SDimitry Andric Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, Diags);
2096344a3780SDimitry Andric }
2097344a3780SDimitry Andric if (!PrintDiag) {
2098344a3780SDimitry Andric assert(!HasError && "expected to report more diagnostics for error");
2099344a3780SDimitry Andric return ErrorReported::reportedOrSuccess(HasError);
2100344a3780SDimitry Andric }
2101e6d15924SDimitry Andric
2102344a3780SDimitry Andric // Print "not found" diagnostic, except that's implied if we already printed a
2103344a3780SDimitry Andric // pattern error.
2104344a3780SDimitry Andric if (!HasPatternError) {
2105d8e91e46SDimitry Andric std::string Message = formatv("{0}: {1} string not found in input",
2106d8e91e46SDimitry Andric Pat.getCheckTy().getDescription(Prefix),
2107d8e91e46SDimitry Andric (ExpectedMatch ? "expected" : "excluded"))
2108d8e91e46SDimitry Andric .str();
2109d8e91e46SDimitry Andric if (Pat.getCount() > 1)
2110344a3780SDimitry Andric Message +=
2111344a3780SDimitry Andric formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();
2112344a3780SDimitry Andric SM.PrintMessage(Loc,
2113344a3780SDimitry Andric ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark,
2114344a3780SDimitry Andric Message);
2115344a3780SDimitry Andric SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note,
2116344a3780SDimitry Andric "scanning from here");
2117d8e91e46SDimitry Andric }
2118d8e91e46SDimitry Andric
2119344a3780SDimitry Andric // Print additional information, which can be useful even after a pattern
2120344a3780SDimitry Andric // error.
2121344a3780SDimitry Andric Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, nullptr);
2122344a3780SDimitry Andric if (ExpectedMatch)
2123344a3780SDimitry Andric Pat.printFuzzyMatch(SM, Buffer, Diags);
2124344a3780SDimitry Andric return ErrorReported::reportedOrSuccess(HasError);
2125344a3780SDimitry Andric }
2126344a3780SDimitry Andric
2127344a3780SDimitry Andric /// Returns either (1) \c ErrorSuccess if there was no error, or (2)
2128344a3780SDimitry Andric /// \c ErrorReported if an error was reported.
reportMatchResult(bool ExpectedMatch,const SourceMgr & SM,StringRef Prefix,SMLoc Loc,const Pattern & Pat,int MatchedCount,StringRef Buffer,Pattern::MatchResult MatchResult,const FileCheckRequest & Req,std::vector<FileCheckDiag> * Diags)2129344a3780SDimitry Andric static Error reportMatchResult(bool ExpectedMatch, const SourceMgr &SM,
2130344a3780SDimitry Andric StringRef Prefix, SMLoc Loc, const Pattern &Pat,
2131344a3780SDimitry Andric int MatchedCount, StringRef Buffer,
2132344a3780SDimitry Andric Pattern::MatchResult MatchResult,
2133344a3780SDimitry Andric const FileCheckRequest &Req,
2134344a3780SDimitry Andric std::vector<FileCheckDiag> *Diags) {
2135344a3780SDimitry Andric if (MatchResult.TheMatch)
2136344a3780SDimitry Andric return printMatch(ExpectedMatch, SM, Prefix, Loc, Pat, MatchedCount, Buffer,
2137344a3780SDimitry Andric std::move(MatchResult), Req, Diags);
2138344a3780SDimitry Andric return printNoMatch(ExpectedMatch, SM, Prefix, Loc, Pat, MatchedCount, Buffer,
2139344a3780SDimitry Andric std::move(MatchResult.TheError), Req.VerboseVerbose,
2140344a3780SDimitry Andric Diags);
2141d8e91e46SDimitry Andric }
2142d8e91e46SDimitry Andric
2143e6d15924SDimitry Andric /// Counts the number of newlines in the specified range.
CountNumNewlinesBetween(StringRef Range,const char * & FirstNewLine)2144d8e91e46SDimitry Andric static unsigned CountNumNewlinesBetween(StringRef Range,
2145d8e91e46SDimitry Andric const char *&FirstNewLine) {
2146d8e91e46SDimitry Andric unsigned NumNewLines = 0;
21476f8fc217SDimitry Andric while (true) {
2148d8e91e46SDimitry Andric // Scan for newline.
2149d8e91e46SDimitry Andric Range = Range.substr(Range.find_first_of("\n\r"));
2150d8e91e46SDimitry Andric if (Range.empty())
2151d8e91e46SDimitry Andric return NumNewLines;
2152d8e91e46SDimitry Andric
2153d8e91e46SDimitry Andric ++NumNewLines;
2154d8e91e46SDimitry Andric
2155d8e91e46SDimitry Andric // Handle \n\r and \r\n as a single newline.
2156d8e91e46SDimitry Andric if (Range.size() > 1 && (Range[1] == '\n' || Range[1] == '\r') &&
2157d8e91e46SDimitry Andric (Range[0] != Range[1]))
2158d8e91e46SDimitry Andric Range = Range.substr(1);
2159d8e91e46SDimitry Andric Range = Range.substr(1);
2160d8e91e46SDimitry Andric
2161d8e91e46SDimitry Andric if (NumNewLines == 1)
2162d8e91e46SDimitry Andric FirstNewLine = Range.begin();
2163d8e91e46SDimitry Andric }
2164d8e91e46SDimitry Andric }
2165d8e91e46SDimitry Andric
Check(const SourceMgr & SM,StringRef Buffer,bool IsLabelScanMode,size_t & MatchLen,FileCheckRequest & Req,std::vector<FileCheckDiag> * Diags) const2166d8e91e46SDimitry Andric size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
2167d8e91e46SDimitry Andric bool IsLabelScanMode, size_t &MatchLen,
2168d8e91e46SDimitry Andric FileCheckRequest &Req,
2169d8e91e46SDimitry Andric std::vector<FileCheckDiag> *Diags) const {
2170d8e91e46SDimitry Andric size_t LastPos = 0;
21714df029ccSDimitry Andric std::vector<const DagNotPrefixInfo *> NotStrings;
2172d8e91e46SDimitry Andric
2173d8e91e46SDimitry Andric // IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL
2174d8e91e46SDimitry Andric // bounds; we have not processed variable definitions within the bounded block
2175d8e91e46SDimitry Andric // yet so cannot handle any final CHECK-DAG yet; this is handled when going
2176d8e91e46SDimitry Andric // over the block again (including the last CHECK-LABEL) in normal mode.
2177d8e91e46SDimitry Andric if (!IsLabelScanMode) {
2178d8e91e46SDimitry Andric // Match "dag strings" (with mixed "not strings" if any).
2179e6d15924SDimitry Andric LastPos = CheckDag(SM, Buffer, NotStrings, Req, Diags);
2180d8e91e46SDimitry Andric if (LastPos == StringRef::npos)
2181d8e91e46SDimitry Andric return StringRef::npos;
2182d8e91e46SDimitry Andric }
2183d8e91e46SDimitry Andric
2184d8e91e46SDimitry Andric // Match itself from the last position after matching CHECK-DAG.
2185d8e91e46SDimitry Andric size_t LastMatchEnd = LastPos;
2186d8e91e46SDimitry Andric size_t FirstMatchPos = 0;
2187d8e91e46SDimitry Andric // Go match the pattern Count times. Majority of patterns only match with
2188d8e91e46SDimitry Andric // count 1 though.
2189d8e91e46SDimitry Andric assert(Pat.getCount() != 0 && "pattern count can not be zero");
2190d8e91e46SDimitry Andric for (int i = 1; i <= Pat.getCount(); i++) {
2191d8e91e46SDimitry Andric StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
2192d8e91e46SDimitry Andric // get a match at current start point
2193344a3780SDimitry Andric Pattern::MatchResult MatchResult = Pat.match(MatchBuffer, SM);
2194d8e91e46SDimitry Andric
2195d8e91e46SDimitry Andric // report
2196344a3780SDimitry Andric if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, Prefix, Loc,
2197344a3780SDimitry Andric Pat, i, MatchBuffer,
2198344a3780SDimitry Andric std::move(MatchResult), Req, Diags)) {
2199344a3780SDimitry Andric cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
2200d8e91e46SDimitry Andric return StringRef::npos;
2201d8e91e46SDimitry Andric }
2202344a3780SDimitry Andric
2203344a3780SDimitry Andric size_t MatchPos = MatchResult.TheMatch->Pos;
2204e6d15924SDimitry Andric if (i == 1)
2205e6d15924SDimitry Andric FirstMatchPos = LastPos + MatchPos;
2206d8e91e46SDimitry Andric
2207d8e91e46SDimitry Andric // move start point after the match
2208344a3780SDimitry Andric LastMatchEnd += MatchPos + MatchResult.TheMatch->Len;
2209d8e91e46SDimitry Andric }
2210d8e91e46SDimitry Andric // Full match len counts from first match pos.
2211d8e91e46SDimitry Andric MatchLen = LastMatchEnd - FirstMatchPos;
2212d8e91e46SDimitry Andric
2213d8e91e46SDimitry Andric // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT
2214d8e91e46SDimitry Andric // or CHECK-NOT
2215d8e91e46SDimitry Andric if (!IsLabelScanMode) {
2216d8e91e46SDimitry Andric size_t MatchPos = FirstMatchPos - LastPos;
2217d8e91e46SDimitry Andric StringRef MatchBuffer = Buffer.substr(LastPos);
2218d8e91e46SDimitry Andric StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);
2219d8e91e46SDimitry Andric
2220d8e91e46SDimitry Andric // If this check is a "CHECK-NEXT", verify that the previous match was on
2221d8e91e46SDimitry Andric // the previous line (i.e. that there is one newline between them).
2222d8e91e46SDimitry Andric if (CheckNext(SM, SkippedRegion)) {
2223d8e91e46SDimitry Andric ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,
2224d8e91e46SDimitry Andric Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
2225d8e91e46SDimitry Andric Diags, Req.Verbose);
2226d8e91e46SDimitry Andric return StringRef::npos;
2227d8e91e46SDimitry Andric }
2228d8e91e46SDimitry Andric
2229d8e91e46SDimitry Andric // If this check is a "CHECK-SAME", verify that the previous match was on
2230d8e91e46SDimitry Andric // the same line (i.e. that there is no newline between them).
2231d8e91e46SDimitry Andric if (CheckSame(SM, SkippedRegion)) {
2232d8e91e46SDimitry Andric ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,
2233d8e91e46SDimitry Andric Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,
2234d8e91e46SDimitry Andric Diags, Req.Verbose);
2235d8e91e46SDimitry Andric return StringRef::npos;
2236d8e91e46SDimitry Andric }
2237d8e91e46SDimitry Andric
2238d8e91e46SDimitry Andric // If this match had "not strings", verify that they don't exist in the
2239d8e91e46SDimitry Andric // skipped region.
2240e6d15924SDimitry Andric if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
2241d8e91e46SDimitry Andric return StringRef::npos;
2242d8e91e46SDimitry Andric }
2243d8e91e46SDimitry Andric
2244d8e91e46SDimitry Andric return FirstMatchPos;
2245d8e91e46SDimitry Andric }
2246d8e91e46SDimitry Andric
CheckNext(const SourceMgr & SM,StringRef Buffer) const2247d8e91e46SDimitry Andric bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {
2248d8e91e46SDimitry Andric if (Pat.getCheckTy() != Check::CheckNext &&
2249d8e91e46SDimitry Andric Pat.getCheckTy() != Check::CheckEmpty)
2250d8e91e46SDimitry Andric return false;
2251d8e91e46SDimitry Andric
2252d8e91e46SDimitry Andric Twine CheckName =
2253d8e91e46SDimitry Andric Prefix +
2254d8e91e46SDimitry Andric Twine(Pat.getCheckTy() == Check::CheckEmpty ? "-EMPTY" : "-NEXT");
2255d8e91e46SDimitry Andric
2256d8e91e46SDimitry Andric // Count the number of newlines between the previous match and this one.
2257d8e91e46SDimitry Andric const char *FirstNewLine = nullptr;
2258d8e91e46SDimitry Andric unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
2259d8e91e46SDimitry Andric
2260d8e91e46SDimitry Andric if (NumNewLines == 0) {
2261d8e91e46SDimitry Andric SM.PrintMessage(Loc, SourceMgr::DK_Error,
2262d8e91e46SDimitry Andric CheckName + ": is on the same line as previous match");
2263d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
2264d8e91e46SDimitry Andric "'next' match was here");
2265d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
2266d8e91e46SDimitry Andric "previous match ended here");
2267d8e91e46SDimitry Andric return true;
2268d8e91e46SDimitry Andric }
2269d8e91e46SDimitry Andric
2270d8e91e46SDimitry Andric if (NumNewLines != 1) {
2271d8e91e46SDimitry Andric SM.PrintMessage(Loc, SourceMgr::DK_Error,
2272d8e91e46SDimitry Andric CheckName +
2273d8e91e46SDimitry Andric ": is not on the line after the previous match");
2274d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
2275d8e91e46SDimitry Andric "'next' match was here");
2276d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
2277d8e91e46SDimitry Andric "previous match ended here");
2278d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(FirstNewLine), SourceMgr::DK_Note,
2279d8e91e46SDimitry Andric "non-matching line after previous match is here");
2280d8e91e46SDimitry Andric return true;
2281d8e91e46SDimitry Andric }
2282d8e91e46SDimitry Andric
2283d8e91e46SDimitry Andric return false;
2284d8e91e46SDimitry Andric }
2285d8e91e46SDimitry Andric
CheckSame(const SourceMgr & SM,StringRef Buffer) const2286d8e91e46SDimitry Andric bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const {
2287d8e91e46SDimitry Andric if (Pat.getCheckTy() != Check::CheckSame)
2288d8e91e46SDimitry Andric return false;
2289d8e91e46SDimitry Andric
2290d8e91e46SDimitry Andric // Count the number of newlines between the previous match and this one.
2291d8e91e46SDimitry Andric const char *FirstNewLine = nullptr;
2292d8e91e46SDimitry Andric unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);
2293d8e91e46SDimitry Andric
2294d8e91e46SDimitry Andric if (NumNewLines != 0) {
2295d8e91e46SDimitry Andric SM.PrintMessage(Loc, SourceMgr::DK_Error,
2296d8e91e46SDimitry Andric Prefix +
2297d8e91e46SDimitry Andric "-SAME: is not on the same line as the previous match");
2298d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,
2299d8e91e46SDimitry Andric "'next' match was here");
2300d8e91e46SDimitry Andric SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,
2301d8e91e46SDimitry Andric "previous match ended here");
2302d8e91e46SDimitry Andric return true;
2303d8e91e46SDimitry Andric }
2304d8e91e46SDimitry Andric
2305d8e91e46SDimitry Andric return false;
2306d8e91e46SDimitry Andric }
2307d8e91e46SDimitry Andric
CheckNot(const SourceMgr & SM,StringRef Buffer,const std::vector<const DagNotPrefixInfo * > & NotStrings,const FileCheckRequest & Req,std::vector<FileCheckDiag> * Diags) const23084df029ccSDimitry Andric bool FileCheckString::CheckNot(
23094df029ccSDimitry Andric const SourceMgr &SM, StringRef Buffer,
23104df029ccSDimitry Andric const std::vector<const DagNotPrefixInfo *> &NotStrings,
23114df029ccSDimitry Andric const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const {
2312b60736ecSDimitry Andric bool DirectiveFail = false;
23134df029ccSDimitry Andric for (auto NotInfo : NotStrings) {
23144df029ccSDimitry Andric assert((NotInfo->DagNotPat.getCheckTy() == Check::CheckNot) &&
23154df029ccSDimitry Andric "Expect CHECK-NOT!");
23164df029ccSDimitry Andric Pattern::MatchResult MatchResult = NotInfo->DagNotPat.match(Buffer, SM);
23174df029ccSDimitry Andric if (Error Err = reportMatchResult(
23184df029ccSDimitry Andric /*ExpectedMatch=*/false, SM, NotInfo->DagNotPrefix,
23194df029ccSDimitry Andric NotInfo->DagNotPat.getLoc(), NotInfo->DagNotPat, 1, Buffer,
2320344a3780SDimitry Andric std::move(MatchResult), Req, Diags)) {
2321344a3780SDimitry Andric cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
2322344a3780SDimitry Andric DirectiveFail = true;
2323d8e91e46SDimitry Andric continue;
2324d8e91e46SDimitry Andric }
2325d8e91e46SDimitry Andric }
2326b60736ecSDimitry Andric return DirectiveFail;
2327d8e91e46SDimitry Andric }
2328d8e91e46SDimitry Andric
23294df029ccSDimitry Andric size_t
CheckDag(const SourceMgr & SM,StringRef Buffer,std::vector<const DagNotPrefixInfo * > & NotStrings,const FileCheckRequest & Req,std::vector<FileCheckDiag> * Diags) const23304df029ccSDimitry Andric FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
23314df029ccSDimitry Andric std::vector<const DagNotPrefixInfo *> &NotStrings,
2332d8e91e46SDimitry Andric const FileCheckRequest &Req,
2333d8e91e46SDimitry Andric std::vector<FileCheckDiag> *Diags) const {
2334d8e91e46SDimitry Andric if (DagNotStrings.empty())
2335d8e91e46SDimitry Andric return 0;
2336d8e91e46SDimitry Andric
2337d8e91e46SDimitry Andric // The start of the search range.
2338d8e91e46SDimitry Andric size_t StartPos = 0;
2339d8e91e46SDimitry Andric
2340d8e91e46SDimitry Andric struct MatchRange {
2341d8e91e46SDimitry Andric size_t Pos;
2342d8e91e46SDimitry Andric size_t End;
2343d8e91e46SDimitry Andric };
2344d8e91e46SDimitry Andric // A sorted list of ranges for non-overlapping CHECK-DAG matches. Match
2345d8e91e46SDimitry Andric // ranges are erased from this list once they are no longer in the search
2346d8e91e46SDimitry Andric // range.
2347d8e91e46SDimitry Andric std::list<MatchRange> MatchRanges;
2348d8e91e46SDimitry Andric
2349d8e91e46SDimitry Andric // We need PatItr and PatEnd later for detecting the end of a CHECK-DAG
2350d8e91e46SDimitry Andric // group, so we don't use a range-based for loop here.
2351d8e91e46SDimitry Andric for (auto PatItr = DagNotStrings.begin(), PatEnd = DagNotStrings.end();
2352d8e91e46SDimitry Andric PatItr != PatEnd; ++PatItr) {
23534df029ccSDimitry Andric const Pattern &Pat = PatItr->DagNotPat;
23544df029ccSDimitry Andric const StringRef DNPrefix = PatItr->DagNotPrefix;
2355d8e91e46SDimitry Andric assert((Pat.getCheckTy() == Check::CheckDAG ||
2356d8e91e46SDimitry Andric Pat.getCheckTy() == Check::CheckNot) &&
2357d8e91e46SDimitry Andric "Invalid CHECK-DAG or CHECK-NOT!");
2358d8e91e46SDimitry Andric
2359d8e91e46SDimitry Andric if (Pat.getCheckTy() == Check::CheckNot) {
23604df029ccSDimitry Andric NotStrings.push_back(&*PatItr);
2361d8e91e46SDimitry Andric continue;
2362d8e91e46SDimitry Andric }
2363d8e91e46SDimitry Andric
2364d8e91e46SDimitry Andric assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!");
2365d8e91e46SDimitry Andric
2366d8e91e46SDimitry Andric // CHECK-DAG always matches from the start.
2367d8e91e46SDimitry Andric size_t MatchLen = 0, MatchPos = StartPos;
2368d8e91e46SDimitry Andric
2369d8e91e46SDimitry Andric // Search for a match that doesn't overlap a previous match in this
2370d8e91e46SDimitry Andric // CHECK-DAG group.
2371d8e91e46SDimitry Andric for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
2372d8e91e46SDimitry Andric StringRef MatchBuffer = Buffer.substr(MatchPos);
2373344a3780SDimitry Andric Pattern::MatchResult MatchResult = Pat.match(MatchBuffer, SM);
2374d8e91e46SDimitry Andric // With a group of CHECK-DAGs, a single mismatching means the match on
2375d8e91e46SDimitry Andric // that group of CHECK-DAGs fails immediately.
2376344a3780SDimitry Andric if (MatchResult.TheError || Req.VerboseVerbose) {
23774df029ccSDimitry Andric if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, DNPrefix,
2378344a3780SDimitry Andric Pat.getLoc(), Pat, 1, MatchBuffer,
2379344a3780SDimitry Andric std::move(MatchResult), Req, Diags)) {
2380344a3780SDimitry Andric cantFail(
2381344a3780SDimitry Andric handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
2382d8e91e46SDimitry Andric return StringRef::npos;
2383d8e91e46SDimitry Andric }
2384344a3780SDimitry Andric }
2385344a3780SDimitry Andric MatchLen = MatchResult.TheMatch->Len;
2386344a3780SDimitry Andric // Re-calc it as the offset relative to the start of the original
2387344a3780SDimitry Andric // string.
2388344a3780SDimitry Andric MatchPos += MatchResult.TheMatch->Pos;
2389d8e91e46SDimitry Andric MatchRange M{MatchPos, MatchPos + MatchLen};
2390d8e91e46SDimitry Andric if (Req.AllowDeprecatedDagOverlap) {
2391d8e91e46SDimitry Andric // We don't need to track all matches in this mode, so we just maintain
2392d8e91e46SDimitry Andric // one match range that encompasses the current CHECK-DAG group's
2393d8e91e46SDimitry Andric // matches.
2394d8e91e46SDimitry Andric if (MatchRanges.empty())
2395d8e91e46SDimitry Andric MatchRanges.insert(MatchRanges.end(), M);
2396d8e91e46SDimitry Andric else {
2397d8e91e46SDimitry Andric auto Block = MatchRanges.begin();
2398d8e91e46SDimitry Andric Block->Pos = std::min(Block->Pos, M.Pos);
2399d8e91e46SDimitry Andric Block->End = std::max(Block->End, M.End);
2400d8e91e46SDimitry Andric }
2401d8e91e46SDimitry Andric break;
2402d8e91e46SDimitry Andric }
2403d8e91e46SDimitry Andric // Iterate previous matches until overlapping match or insertion point.
2404d8e91e46SDimitry Andric bool Overlap = false;
2405d8e91e46SDimitry Andric for (; MI != ME; ++MI) {
2406d8e91e46SDimitry Andric if (M.Pos < MI->End) {
2407d8e91e46SDimitry Andric // !Overlap => New match has no overlap and is before this old match.
2408d8e91e46SDimitry Andric // Overlap => New match overlaps this old match.
2409d8e91e46SDimitry Andric Overlap = MI->Pos < M.End;
2410d8e91e46SDimitry Andric break;
2411d8e91e46SDimitry Andric }
2412d8e91e46SDimitry Andric }
2413d8e91e46SDimitry Andric if (!Overlap) {
2414d8e91e46SDimitry Andric // Insert non-overlapping match into list.
2415d8e91e46SDimitry Andric MatchRanges.insert(MI, M);
2416d8e91e46SDimitry Andric break;
2417d8e91e46SDimitry Andric }
2418d8e91e46SDimitry Andric if (Req.VerboseVerbose) {
2419e6d15924SDimitry Andric // Due to their verbosity, we don't print verbose diagnostics here if
2420e6d15924SDimitry Andric // we're gathering them for a different rendering, but we always print
2421e6d15924SDimitry Andric // other diagnostics.
2422e6d15924SDimitry Andric if (!Diags) {
2423d8e91e46SDimitry Andric SMLoc OldStart = SMLoc::getFromPointer(Buffer.data() + MI->Pos);
2424d8e91e46SDimitry Andric SMLoc OldEnd = SMLoc::getFromPointer(Buffer.data() + MI->End);
2425d8e91e46SDimitry Andric SMRange OldRange(OldStart, OldEnd);
2426d8e91e46SDimitry Andric SM.PrintMessage(OldStart, SourceMgr::DK_Note,
2427d8e91e46SDimitry Andric "match discarded, overlaps earlier DAG match here",
2428d8e91e46SDimitry Andric {OldRange});
2429b60736ecSDimitry Andric } else {
2430b60736ecSDimitry Andric SMLoc CheckLoc = Diags->rbegin()->CheckLoc;
2431b60736ecSDimitry Andric for (auto I = Diags->rbegin(), E = Diags->rend();
2432b60736ecSDimitry Andric I != E && I->CheckLoc == CheckLoc; ++I)
2433b60736ecSDimitry Andric I->MatchTy = FileCheckDiag::MatchFoundButDiscarded;
2434b60736ecSDimitry Andric }
2435d8e91e46SDimitry Andric }
2436d8e91e46SDimitry Andric MatchPos = MI->End;
2437d8e91e46SDimitry Andric }
2438d8e91e46SDimitry Andric if (!Req.VerboseVerbose)
2439344a3780SDimitry Andric cantFail(printMatch(
24404df029ccSDimitry Andric /*ExpectedMatch=*/true, SM, DNPrefix, Pat.getLoc(), Pat, 1, Buffer,
2441344a3780SDimitry Andric Pattern::MatchResult(MatchPos, MatchLen, Error::success()), Req,
2442344a3780SDimitry Andric Diags));
2443d8e91e46SDimitry Andric
2444d8e91e46SDimitry Andric // Handle the end of a CHECK-DAG group.
2445d8e91e46SDimitry Andric if (std::next(PatItr) == PatEnd ||
24464df029ccSDimitry Andric std::next(PatItr)->DagNotPat.getCheckTy() == Check::CheckNot) {
2447d8e91e46SDimitry Andric if (!NotStrings.empty()) {
2448d8e91e46SDimitry Andric // If there are CHECK-NOTs between two CHECK-DAGs or from CHECK to
2449d8e91e46SDimitry Andric // CHECK-DAG, verify that there are no 'not' strings occurred in that
2450d8e91e46SDimitry Andric // region.
2451d8e91e46SDimitry Andric StringRef SkippedRegion =
2452d8e91e46SDimitry Andric Buffer.slice(StartPos, MatchRanges.begin()->Pos);
2453e6d15924SDimitry Andric if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))
2454d8e91e46SDimitry Andric return StringRef::npos;
2455d8e91e46SDimitry Andric // Clear "not strings".
2456d8e91e46SDimitry Andric NotStrings.clear();
2457d8e91e46SDimitry Andric }
2458d8e91e46SDimitry Andric // All subsequent CHECK-DAGs and CHECK-NOTs should be matched from the
2459d8e91e46SDimitry Andric // end of this CHECK-DAG group's match range.
2460d8e91e46SDimitry Andric StartPos = MatchRanges.rbegin()->End;
2461d8e91e46SDimitry Andric // Don't waste time checking for (impossible) overlaps before that.
2462d8e91e46SDimitry Andric MatchRanges.clear();
2463d8e91e46SDimitry Andric }
2464d8e91e46SDimitry Andric }
2465d8e91e46SDimitry Andric
2466d8e91e46SDimitry Andric return StartPos;
2467d8e91e46SDimitry Andric }
2468d8e91e46SDimitry Andric
ValidatePrefixes(StringRef Kind,StringSet<> & UniquePrefixes,ArrayRef<StringRef> SuppliedPrefixes)2469cfca06d7SDimitry Andric static bool ValidatePrefixes(StringRef Kind, StringSet<> &UniquePrefixes,
2470cfca06d7SDimitry Andric ArrayRef<StringRef> SuppliedPrefixes) {
2471cfca06d7SDimitry Andric for (StringRef Prefix : SuppliedPrefixes) {
2472cfca06d7SDimitry Andric if (Prefix.empty()) {
2473cfca06d7SDimitry Andric errs() << "error: supplied " << Kind << " prefix must not be the empty "
2474cfca06d7SDimitry Andric << "string\n";
2475cfca06d7SDimitry Andric return false;
2476d8e91e46SDimitry Andric }
2477cfca06d7SDimitry Andric static const Regex Validator("^[a-zA-Z0-9_-]*$");
2478cfca06d7SDimitry Andric if (!Validator.match(Prefix)) {
2479cfca06d7SDimitry Andric errs() << "error: supplied " << Kind << " prefix must start with a "
2480cfca06d7SDimitry Andric << "letter and contain only alphanumeric characters, hyphens, and "
2481cfca06d7SDimitry Andric << "underscores: '" << Prefix << "'\n";
2482cfca06d7SDimitry Andric return false;
2483cfca06d7SDimitry Andric }
2484cfca06d7SDimitry Andric if (!UniquePrefixes.insert(Prefix).second) {
2485cfca06d7SDimitry Andric errs() << "error: supplied " << Kind << " prefix must be unique among "
2486cfca06d7SDimitry Andric << "check and comment prefixes: '" << Prefix << "'\n";
2487cfca06d7SDimitry Andric return false;
2488cfca06d7SDimitry Andric }
2489cfca06d7SDimitry Andric }
2490cfca06d7SDimitry Andric return true;
2491cfca06d7SDimitry Andric }
2492cfca06d7SDimitry Andric
ValidateCheckPrefixes()2493e6d15924SDimitry Andric bool FileCheck::ValidateCheckPrefixes() {
2494cfca06d7SDimitry Andric StringSet<> UniquePrefixes;
2495cfca06d7SDimitry Andric // Add default prefixes to catch user-supplied duplicates of them below.
2496cfca06d7SDimitry Andric if (Req.CheckPrefixes.empty()) {
2497cfca06d7SDimitry Andric for (const char *Prefix : DefaultCheckPrefixes)
2498cfca06d7SDimitry Andric UniquePrefixes.insert(Prefix);
2499d8e91e46SDimitry Andric }
2500cfca06d7SDimitry Andric if (Req.CommentPrefixes.empty()) {
2501cfca06d7SDimitry Andric for (const char *Prefix : DefaultCommentPrefixes)
2502cfca06d7SDimitry Andric UniquePrefixes.insert(Prefix);
2503cfca06d7SDimitry Andric }
2504cfca06d7SDimitry Andric // Do not validate the default prefixes, or diagnostics about duplicates might
2505cfca06d7SDimitry Andric // incorrectly indicate that they were supplied by the user.
2506cfca06d7SDimitry Andric if (!ValidatePrefixes("check", UniquePrefixes, Req.CheckPrefixes))
2507cfca06d7SDimitry Andric return false;
2508cfca06d7SDimitry Andric if (!ValidatePrefixes("comment", UniquePrefixes, Req.CommentPrefixes))
2509cfca06d7SDimitry Andric return false;
2510d8e91e46SDimitry Andric return true;
2511d8e91e46SDimitry Andric }
2512d8e91e46SDimitry Andric
defineCmdlineVariables(ArrayRef<StringRef> CmdlineDefines,SourceMgr & SM)2513e6d15924SDimitry Andric Error FileCheckPatternContext::defineCmdlineVariables(
2514cfca06d7SDimitry Andric ArrayRef<StringRef> CmdlineDefines, SourceMgr &SM) {
2515e6d15924SDimitry Andric assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&
2516e6d15924SDimitry Andric "Overriding defined variable with command-line variable definitions");
2517d8e91e46SDimitry Andric
2518e6d15924SDimitry Andric if (CmdlineDefines.empty())
2519e6d15924SDimitry Andric return Error::success();
2520e6d15924SDimitry Andric
2521e6d15924SDimitry Andric // Create a string representing the vector of command-line definitions. Each
2522e6d15924SDimitry Andric // definition is on its own line and prefixed with a definition number to
2523e6d15924SDimitry Andric // clarify which definition a given diagnostic corresponds to.
2524e6d15924SDimitry Andric unsigned I = 0;
2525e6d15924SDimitry Andric Error Errs = Error::success();
2526e6d15924SDimitry Andric std::string CmdlineDefsDiag;
25271d5ae102SDimitry Andric SmallVector<std::pair<size_t, size_t>, 4> CmdlineDefsIndices;
25281d5ae102SDimitry Andric for (StringRef CmdlineDef : CmdlineDefines) {
25291d5ae102SDimitry Andric std::string DefPrefix = ("Global define #" + Twine(++I) + ": ").str();
25301d5ae102SDimitry Andric size_t EqIdx = CmdlineDef.find('=');
25311d5ae102SDimitry Andric if (EqIdx == StringRef::npos) {
25321d5ae102SDimitry Andric CmdlineDefsIndices.push_back(std::make_pair(CmdlineDefsDiag.size(), 0));
25331d5ae102SDimitry Andric continue;
25341d5ae102SDimitry Andric }
25351d5ae102SDimitry Andric // Numeric variable definition.
25361d5ae102SDimitry Andric if (CmdlineDef[0] == '#') {
25371d5ae102SDimitry Andric // Append a copy of the command-line definition adapted to use the same
25381d5ae102SDimitry Andric // format as in the input file to be able to reuse
25391d5ae102SDimitry Andric // parseNumericSubstitutionBlock.
25401d5ae102SDimitry Andric CmdlineDefsDiag += (DefPrefix + CmdlineDef + " (parsed as: [[").str();
2541cfca06d7SDimitry Andric std::string SubstitutionStr = std::string(CmdlineDef);
25421d5ae102SDimitry Andric SubstitutionStr[EqIdx] = ':';
25431d5ae102SDimitry Andric CmdlineDefsIndices.push_back(
25441d5ae102SDimitry Andric std::make_pair(CmdlineDefsDiag.size(), SubstitutionStr.size()));
25451d5ae102SDimitry Andric CmdlineDefsDiag += (SubstitutionStr + Twine("]])\n")).str();
25461d5ae102SDimitry Andric } else {
25471d5ae102SDimitry Andric CmdlineDefsDiag += DefPrefix;
25481d5ae102SDimitry Andric CmdlineDefsIndices.push_back(
25491d5ae102SDimitry Andric std::make_pair(CmdlineDefsDiag.size(), CmdlineDef.size()));
25501d5ae102SDimitry Andric CmdlineDefsDiag += (CmdlineDef + "\n").str();
25511d5ae102SDimitry Andric }
25521d5ae102SDimitry Andric }
2553e6d15924SDimitry Andric
2554e6d15924SDimitry Andric // Create a buffer with fake command line content in order to display
2555e6d15924SDimitry Andric // parsing diagnostic with location information and point to the
2556e6d15924SDimitry Andric // global definition with invalid syntax.
2557e6d15924SDimitry Andric std::unique_ptr<MemoryBuffer> CmdLineDefsDiagBuffer =
2558e6d15924SDimitry Andric MemoryBuffer::getMemBufferCopy(CmdlineDefsDiag, "Global defines");
2559e6d15924SDimitry Andric StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();
2560e6d15924SDimitry Andric SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc());
2561e6d15924SDimitry Andric
25621d5ae102SDimitry Andric for (std::pair<size_t, size_t> CmdlineDefIndices : CmdlineDefsIndices) {
25631d5ae102SDimitry Andric StringRef CmdlineDef = CmdlineDefsDiagRef.substr(CmdlineDefIndices.first,
25641d5ae102SDimitry Andric CmdlineDefIndices.second);
25651d5ae102SDimitry Andric if (CmdlineDef.empty()) {
2566e6d15924SDimitry Andric Errs = joinErrors(
2567e6d15924SDimitry Andric std::move(Errs),
2568706b4fc4SDimitry Andric ErrorDiagnostic::get(SM, CmdlineDef,
2569706b4fc4SDimitry Andric "missing equal sign in global definition"));
2570e6d15924SDimitry Andric continue;
2571d8e91e46SDimitry Andric }
2572d8e91e46SDimitry Andric
2573e6d15924SDimitry Andric // Numeric variable definition.
2574e6d15924SDimitry Andric if (CmdlineDef[0] == '#') {
25751d5ae102SDimitry Andric // Now parse the definition both to check that the syntax is correct and
25761d5ae102SDimitry Andric // to create the necessary class instance.
25771d5ae102SDimitry Andric StringRef CmdlineDefExpr = CmdlineDef.substr(1);
2578e3b55780SDimitry Andric std::optional<NumericVariable *> DefinedNumericVariable;
2579cfca06d7SDimitry Andric Expected<std::unique_ptr<Expression>> ExpressionResult =
2580e3b55780SDimitry Andric Pattern::parseNumericSubstitutionBlock(CmdlineDefExpr,
2581e3b55780SDimitry Andric DefinedNumericVariable, false,
2582e3b55780SDimitry Andric std::nullopt, this, SM);
2583cfca06d7SDimitry Andric if (!ExpressionResult) {
2584cfca06d7SDimitry Andric Errs = joinErrors(std::move(Errs), ExpressionResult.takeError());
25851d5ae102SDimitry Andric continue;
25861d5ae102SDimitry Andric }
2587cfca06d7SDimitry Andric std::unique_ptr<Expression> Expression = std::move(*ExpressionResult);
25881d5ae102SDimitry Andric // Now evaluate the expression whose value this variable should be set
25891d5ae102SDimitry Andric // to, since the expression of a command-line variable definition should
25901d5ae102SDimitry Andric // only use variables defined earlier on the command-line. If not, this
25911d5ae102SDimitry Andric // is an error and we report it.
2592b1c73532SDimitry Andric Expected<APInt> Value = Expression->getAST()->eval();
25931d5ae102SDimitry Andric if (!Value) {
25941d5ae102SDimitry Andric Errs = joinErrors(std::move(Errs), Value.takeError());
2595e6d15924SDimitry Andric continue;
2596e6d15924SDimitry Andric }
2597e6d15924SDimitry Andric
25981d5ae102SDimitry Andric assert(DefinedNumericVariable && "No variable defined");
25991d5ae102SDimitry Andric (*DefinedNumericVariable)->setValue(*Value);
2600e6d15924SDimitry Andric
2601e6d15924SDimitry Andric // Record this variable definition.
26021d5ae102SDimitry Andric GlobalNumericVariableTable[(*DefinedNumericVariable)->getName()] =
26031d5ae102SDimitry Andric *DefinedNumericVariable;
2604e6d15924SDimitry Andric } else {
2605e6d15924SDimitry Andric // String variable definition.
2606e6d15924SDimitry Andric std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
2607e6d15924SDimitry Andric StringRef CmdlineName = CmdlineNameVal.first;
2608e6d15924SDimitry Andric StringRef OrigCmdlineName = CmdlineName;
2609706b4fc4SDimitry Andric Expected<Pattern::VariableProperties> ParseVarResult =
2610706b4fc4SDimitry Andric Pattern::parseVariable(CmdlineName, SM);
2611e6d15924SDimitry Andric if (!ParseVarResult) {
2612e6d15924SDimitry Andric Errs = joinErrors(std::move(Errs), ParseVarResult.takeError());
2613e6d15924SDimitry Andric continue;
2614e6d15924SDimitry Andric }
2615e6d15924SDimitry Andric // Check that CmdlineName does not denote a pseudo variable is only
2616e6d15924SDimitry Andric // composed of the parsed numeric variable. This catches cases like
2617e6d15924SDimitry Andric // "FOO+2" in a "FOO+2=10" definition.
2618e6d15924SDimitry Andric if (ParseVarResult->IsPseudo || !CmdlineName.empty()) {
2619e6d15924SDimitry Andric Errs = joinErrors(std::move(Errs),
2620706b4fc4SDimitry Andric ErrorDiagnostic::get(
2621e6d15924SDimitry Andric SM, OrigCmdlineName,
2622e6d15924SDimitry Andric "invalid name in string variable definition '" +
2623e6d15924SDimitry Andric OrigCmdlineName + "'"));
2624e6d15924SDimitry Andric continue;
2625e6d15924SDimitry Andric }
2626e6d15924SDimitry Andric StringRef Name = ParseVarResult->Name;
2627e6d15924SDimitry Andric
2628e6d15924SDimitry Andric // Detect collisions between string and numeric variables when the former
2629e6d15924SDimitry Andric // is created later than the latter.
26307fa27ce4SDimitry Andric if (GlobalNumericVariableTable.contains(Name)) {
2631706b4fc4SDimitry Andric Errs = joinErrors(std::move(Errs),
2632706b4fc4SDimitry Andric ErrorDiagnostic::get(SM, Name,
2633e6d15924SDimitry Andric "numeric variable with name '" +
2634e6d15924SDimitry Andric Name + "' already exists"));
2635e6d15924SDimitry Andric continue;
2636e6d15924SDimitry Andric }
2637e6d15924SDimitry Andric GlobalVariableTable.insert(CmdlineNameVal);
2638e6d15924SDimitry Andric // Mark the string variable as defined to detect collisions between
26391d5ae102SDimitry Andric // string and numeric variables in defineCmdlineVariables when the latter
2640e6d15924SDimitry Andric // is created later than the former. We cannot reuse GlobalVariableTable
2641e6d15924SDimitry Andric // for this by populating it with an empty string since we would then
2642e6d15924SDimitry Andric // lose the ability to detect the use of an undefined variable in
2643e6d15924SDimitry Andric // match().
2644e6d15924SDimitry Andric DefinedVariableTable[Name] = true;
2645e6d15924SDimitry Andric }
2646e6d15924SDimitry Andric }
2647e6d15924SDimitry Andric
2648e6d15924SDimitry Andric return Errs;
2649e6d15924SDimitry Andric }
2650e6d15924SDimitry Andric
clearLocalVars()2651e6d15924SDimitry Andric void FileCheckPatternContext::clearLocalVars() {
2652e6d15924SDimitry Andric SmallVector<StringRef, 16> LocalPatternVars, LocalNumericVars;
2653e6d15924SDimitry Andric for (const StringMapEntry<StringRef> &Var : GlobalVariableTable)
2654e6d15924SDimitry Andric if (Var.first()[0] != '$')
2655e6d15924SDimitry Andric LocalPatternVars.push_back(Var.first());
2656e6d15924SDimitry Andric
2657e6d15924SDimitry Andric // Numeric substitution reads the value of a variable directly, not via
2658e6d15924SDimitry Andric // GlobalNumericVariableTable. Therefore, we clear local variables by
2659e6d15924SDimitry Andric // clearing their value which will lead to a numeric substitution failure. We
2660e6d15924SDimitry Andric // also mark the variable for removal from GlobalNumericVariableTable since
2661e6d15924SDimitry Andric // this is what defineCmdlineVariables checks to decide that no global
2662e6d15924SDimitry Andric // variable has been defined.
2663e6d15924SDimitry Andric for (const auto &Var : GlobalNumericVariableTable)
2664e6d15924SDimitry Andric if (Var.first()[0] != '$') {
2665e6d15924SDimitry Andric Var.getValue()->clearValue();
2666e6d15924SDimitry Andric LocalNumericVars.push_back(Var.first());
2667e6d15924SDimitry Andric }
2668e6d15924SDimitry Andric
2669e6d15924SDimitry Andric for (const auto &Var : LocalPatternVars)
2670e6d15924SDimitry Andric GlobalVariableTable.erase(Var);
2671e6d15924SDimitry Andric for (const auto &Var : LocalNumericVars)
2672e6d15924SDimitry Andric GlobalNumericVariableTable.erase(Var);
2673e6d15924SDimitry Andric }
2674e6d15924SDimitry Andric
checkInput(SourceMgr & SM,StringRef Buffer,std::vector<FileCheckDiag> * Diags)26751d5ae102SDimitry Andric bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,
2676d8e91e46SDimitry Andric std::vector<FileCheckDiag> *Diags) {
2677d8e91e46SDimitry Andric bool ChecksFailed = false;
2678d8e91e46SDimitry Andric
26791d5ae102SDimitry Andric unsigned i = 0, j = 0, e = CheckStrings->size();
2680d8e91e46SDimitry Andric while (true) {
2681d8e91e46SDimitry Andric StringRef CheckRegion;
2682d8e91e46SDimitry Andric if (j == e) {
2683d8e91e46SDimitry Andric CheckRegion = Buffer;
2684d8e91e46SDimitry Andric } else {
26851d5ae102SDimitry Andric const FileCheckString &CheckLabelStr = (*CheckStrings)[j];
2686d8e91e46SDimitry Andric if (CheckLabelStr.Pat.getCheckTy() != Check::CheckLabel) {
2687d8e91e46SDimitry Andric ++j;
2688d8e91e46SDimitry Andric continue;
2689d8e91e46SDimitry Andric }
2690d8e91e46SDimitry Andric
2691d8e91e46SDimitry Andric // Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG
2692d8e91e46SDimitry Andric size_t MatchLabelLen = 0;
2693e6d15924SDimitry Andric size_t MatchLabelPos =
2694e6d15924SDimitry Andric CheckLabelStr.Check(SM, Buffer, true, MatchLabelLen, Req, Diags);
2695d8e91e46SDimitry Andric if (MatchLabelPos == StringRef::npos)
2696e6d15924SDimitry Andric // Immediately bail if CHECK-LABEL fails, nothing else we can do.
2697d8e91e46SDimitry Andric return false;
2698d8e91e46SDimitry Andric
2699d8e91e46SDimitry Andric CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen);
2700d8e91e46SDimitry Andric Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen);
2701d8e91e46SDimitry Andric ++j;
2702d8e91e46SDimitry Andric }
2703d8e91e46SDimitry Andric
2704e6d15924SDimitry Andric // Do not clear the first region as it's the one before the first
2705e6d15924SDimitry Andric // CHECK-LABEL and it would clear variables defined on the command-line
2706e6d15924SDimitry Andric // before they get used.
2707e6d15924SDimitry Andric if (i != 0 && Req.EnableVarScope)
27081d5ae102SDimitry Andric PatternContext->clearLocalVars();
2709d8e91e46SDimitry Andric
2710d8e91e46SDimitry Andric for (; i != j; ++i) {
27111d5ae102SDimitry Andric const FileCheckString &CheckStr = (*CheckStrings)[i];
2712d8e91e46SDimitry Andric
2713d8e91e46SDimitry Andric // Check each string within the scanned region, including a second check
2714d8e91e46SDimitry Andric // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)
2715d8e91e46SDimitry Andric size_t MatchLen = 0;
2716e6d15924SDimitry Andric size_t MatchPos =
2717e6d15924SDimitry Andric CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);
2718d8e91e46SDimitry Andric
2719d8e91e46SDimitry Andric if (MatchPos == StringRef::npos) {
2720d8e91e46SDimitry Andric ChecksFailed = true;
2721d8e91e46SDimitry Andric i = j;
2722d8e91e46SDimitry Andric break;
2723d8e91e46SDimitry Andric }
2724d8e91e46SDimitry Andric
2725d8e91e46SDimitry Andric CheckRegion = CheckRegion.substr(MatchPos + MatchLen);
2726d8e91e46SDimitry Andric }
2727d8e91e46SDimitry Andric
2728d8e91e46SDimitry Andric if (j == e)
2729d8e91e46SDimitry Andric break;
2730d8e91e46SDimitry Andric }
2731d8e91e46SDimitry Andric
2732d8e91e46SDimitry Andric // Success if no checks failed.
2733d8e91e46SDimitry Andric return !ChecksFailed;
2734d8e91e46SDimitry Andric }
2735