Path: blob/main/contrib/llvm-project/llvm/lib/FileCheck/FileCheck.cpp
35232 views
//===- FileCheck.cpp - Check that File's Contents match what is expected --===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// FileCheck does a line-by line check of a file that validates whether it9// contains the expected content. This is useful for regression tests etc.10//11// This file implements most of the API that will be used by the FileCheck utility12// as well as various unittests.13//===----------------------------------------------------------------------===//1415#include "llvm/FileCheck/FileCheck.h"16#include "FileCheckImpl.h"17#include "llvm/ADT/STLExtras.h"18#include "llvm/ADT/StringExtras.h"19#include "llvm/ADT/StringSet.h"20#include "llvm/ADT/Twine.h"21#include "llvm/Support/CheckedArithmetic.h"22#include "llvm/Support/FormatVariadic.h"23#include <cstdint>24#include <list>25#include <set>26#include <tuple>27#include <utility>2829using namespace llvm;3031StringRef ExpressionFormat::toString() const {32switch (Value) {33case Kind::NoFormat:34return StringRef("<none>");35case Kind::Unsigned:36return StringRef("%u");37case Kind::Signed:38return StringRef("%d");39case Kind::HexUpper:40return StringRef("%X");41case Kind::HexLower:42return StringRef("%x");43}44llvm_unreachable("unknown expression format");45}4647Expected<std::string> ExpressionFormat::getWildcardRegex() const {48StringRef AlternateFormPrefix = AlternateForm ? StringRef("0x") : StringRef();4950auto CreatePrecisionRegex = [&](StringRef S) {51return (Twine(AlternateFormPrefix) + S + Twine('{') + Twine(Precision) +52"}")53.str();54};5556switch (Value) {57case Kind::Unsigned:58if (Precision)59return CreatePrecisionRegex("([1-9][0-9]*)?[0-9]");60return std::string("[0-9]+");61case Kind::Signed:62if (Precision)63return CreatePrecisionRegex("-?([1-9][0-9]*)?[0-9]");64return std::string("-?[0-9]+");65case Kind::HexUpper:66if (Precision)67return CreatePrecisionRegex("([1-9A-F][0-9A-F]*)?[0-9A-F]");68return (Twine(AlternateFormPrefix) + Twine("[0-9A-F]+")).str();69case Kind::HexLower:70if (Precision)71return CreatePrecisionRegex("([1-9a-f][0-9a-f]*)?[0-9a-f]");72return (Twine(AlternateFormPrefix) + Twine("[0-9a-f]+")).str();73default:74return createStringError(std::errc::invalid_argument,75"trying to match value with invalid format");76}77}7879Expected<std::string>80ExpressionFormat::getMatchingString(APInt IntValue) const {81if (Value != Kind::Signed && IntValue.isNegative())82return make_error<OverflowError>();8384unsigned Radix;85bool UpperCase = false;86SmallString<8> AbsoluteValueStr;87StringRef SignPrefix = IntValue.isNegative() ? "-" : "";88switch (Value) {89case Kind::Unsigned:90case Kind::Signed:91Radix = 10;92break;93case Kind::HexUpper:94UpperCase = true;95Radix = 16;96break;97case Kind::HexLower:98Radix = 16;99UpperCase = false;100break;101default:102return createStringError(std::errc::invalid_argument,103"trying to match value with invalid format");104}105IntValue.abs().toString(AbsoluteValueStr, Radix, /*Signed=*/false,106/*formatAsCLiteral=*/false,107/*UpperCase=*/UpperCase);108109StringRef AlternateFormPrefix = AlternateForm ? StringRef("0x") : StringRef();110111if (Precision > AbsoluteValueStr.size()) {112unsigned LeadingZeros = Precision - AbsoluteValueStr.size();113return (Twine(SignPrefix) + Twine(AlternateFormPrefix) +114std::string(LeadingZeros, '0') + AbsoluteValueStr)115.str();116}117118return (Twine(SignPrefix) + Twine(AlternateFormPrefix) + AbsoluteValueStr)119.str();120}121122static unsigned nextAPIntBitWidth(unsigned BitWidth) {123return (BitWidth < APInt::APINT_BITS_PER_WORD) ? APInt::APINT_BITS_PER_WORD124: BitWidth * 2;125}126127static APInt toSigned(APInt AbsVal, bool Negative) {128if (AbsVal.isSignBitSet())129AbsVal = AbsVal.zext(nextAPIntBitWidth(AbsVal.getBitWidth()));130APInt Result = AbsVal;131if (Negative)132Result.negate();133return Result;134}135136APInt ExpressionFormat::valueFromStringRepr(StringRef StrVal,137const SourceMgr &SM) const {138bool ValueIsSigned = Value == Kind::Signed;139bool Negative = StrVal.consume_front("-");140bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;141bool MissingFormPrefix =142!ValueIsSigned && AlternateForm && !StrVal.consume_front("0x");143(void)MissingFormPrefix;144assert(!MissingFormPrefix && "missing alternate form prefix");145APInt ResultValue;146[[maybe_unused]] bool ParseFailure =147StrVal.getAsInteger(Hex ? 16 : 10, ResultValue);148// Both the FileCheck utility and library only call this method with a valid149// value in StrVal. This is guaranteed by the regex returned by150// getWildcardRegex() above.151assert(!ParseFailure && "unable to represent numeric value");152return toSigned(ResultValue, Negative);153}154155Expected<APInt> llvm::exprAdd(const APInt &LeftOperand,156const APInt &RightOperand, bool &Overflow) {157return LeftOperand.sadd_ov(RightOperand, Overflow);158}159160Expected<APInt> llvm::exprSub(const APInt &LeftOperand,161const APInt &RightOperand, bool &Overflow) {162return LeftOperand.ssub_ov(RightOperand, Overflow);163}164165Expected<APInt> llvm::exprMul(const APInt &LeftOperand,166const APInt &RightOperand, bool &Overflow) {167return LeftOperand.smul_ov(RightOperand, Overflow);168}169170Expected<APInt> llvm::exprDiv(const APInt &LeftOperand,171const APInt &RightOperand, bool &Overflow) {172// Check for division by zero.173if (RightOperand.isZero())174return make_error<OverflowError>();175176return LeftOperand.sdiv_ov(RightOperand, Overflow);177}178179Expected<APInt> llvm::exprMax(const APInt &LeftOperand,180const APInt &RightOperand, bool &Overflow) {181Overflow = false;182return LeftOperand.slt(RightOperand) ? RightOperand : LeftOperand;183}184185Expected<APInt> llvm::exprMin(const APInt &LeftOperand,186const APInt &RightOperand, bool &Overflow) {187Overflow = false;188if (cantFail(exprMax(LeftOperand, RightOperand, Overflow)) == LeftOperand)189return RightOperand;190191return LeftOperand;192}193194Expected<APInt> NumericVariableUse::eval() const {195std::optional<APInt> Value = Variable->getValue();196if (Value)197return *Value;198199return make_error<UndefVarError>(getExpressionStr());200}201202Expected<APInt> BinaryOperation::eval() const {203Expected<APInt> MaybeLeftOp = LeftOperand->eval();204Expected<APInt> MaybeRightOp = RightOperand->eval();205206// Bubble up any error (e.g. undefined variables) in the recursive207// evaluation.208if (!MaybeLeftOp || !MaybeRightOp) {209Error Err = Error::success();210if (!MaybeLeftOp)211Err = joinErrors(std::move(Err), MaybeLeftOp.takeError());212if (!MaybeRightOp)213Err = joinErrors(std::move(Err), MaybeRightOp.takeError());214return std::move(Err);215}216217APInt LeftOp = *MaybeLeftOp;218APInt RightOp = *MaybeRightOp;219bool Overflow;220// Ensure both operands have the same bitwidth.221unsigned LeftBitWidth = LeftOp.getBitWidth();222unsigned RightBitWidth = RightOp.getBitWidth();223unsigned NewBitWidth = std::max(LeftBitWidth, RightBitWidth);224LeftOp = LeftOp.sext(NewBitWidth);225RightOp = RightOp.sext(NewBitWidth);226do {227Expected<APInt> MaybeResult = EvalBinop(LeftOp, RightOp, Overflow);228if (!MaybeResult)229return MaybeResult.takeError();230231if (!Overflow)232return MaybeResult;233234NewBitWidth = nextAPIntBitWidth(NewBitWidth);235LeftOp = LeftOp.sext(NewBitWidth);236RightOp = RightOp.sext(NewBitWidth);237} while (true);238}239240Expected<ExpressionFormat>241BinaryOperation::getImplicitFormat(const SourceMgr &SM) const {242Expected<ExpressionFormat> LeftFormat = LeftOperand->getImplicitFormat(SM);243Expected<ExpressionFormat> RightFormat = RightOperand->getImplicitFormat(SM);244if (!LeftFormat || !RightFormat) {245Error Err = Error::success();246if (!LeftFormat)247Err = joinErrors(std::move(Err), LeftFormat.takeError());248if (!RightFormat)249Err = joinErrors(std::move(Err), RightFormat.takeError());250return std::move(Err);251}252253if (*LeftFormat != ExpressionFormat::Kind::NoFormat &&254*RightFormat != ExpressionFormat::Kind::NoFormat &&255*LeftFormat != *RightFormat)256return ErrorDiagnostic::get(257SM, getExpressionStr(),258"implicit format conflict between '" + LeftOperand->getExpressionStr() +259"' (" + LeftFormat->toString() + ") and '" +260RightOperand->getExpressionStr() + "' (" + RightFormat->toString() +261"), need an explicit format specifier");262263return *LeftFormat != ExpressionFormat::Kind::NoFormat ? *LeftFormat264: *RightFormat;265}266267Expected<std::string> NumericSubstitution::getResult() const {268assert(ExpressionPointer->getAST() != nullptr &&269"Substituting empty expression");270Expected<APInt> EvaluatedValue = ExpressionPointer->getAST()->eval();271if (!EvaluatedValue)272return EvaluatedValue.takeError();273ExpressionFormat Format = ExpressionPointer->getFormat();274return Format.getMatchingString(*EvaluatedValue);275}276277Expected<std::string> StringSubstitution::getResult() const {278// Look up the value and escape it so that we can put it into the regex.279Expected<StringRef> VarVal = Context->getPatternVarValue(FromStr);280if (!VarVal)281return VarVal.takeError();282return Regex::escape(*VarVal);283}284285bool Pattern::isValidVarNameStart(char C) { return C == '_' || isAlpha(C); }286287Expected<Pattern::VariableProperties>288Pattern::parseVariable(StringRef &Str, const SourceMgr &SM) {289if (Str.empty())290return ErrorDiagnostic::get(SM, Str, "empty variable name");291292size_t I = 0;293bool IsPseudo = Str[0] == '@';294295// Global vars start with '$'.296if (Str[0] == '$' || IsPseudo)297++I;298299if (I == Str.size())300return ErrorDiagnostic::get(SM, Str.slice(I, StringRef::npos),301StringRef("empty ") +302(IsPseudo ? "pseudo " : "global ") +303"variable name");304305if (!isValidVarNameStart(Str[I++]))306return ErrorDiagnostic::get(SM, Str, "invalid variable name");307308for (size_t E = Str.size(); I != E; ++I)309// Variable names are composed of alphanumeric characters and underscores.310if (Str[I] != '_' && !isAlnum(Str[I]))311break;312313StringRef Name = Str.take_front(I);314Str = Str.substr(I);315return VariableProperties {Name, IsPseudo};316}317318// StringRef holding all characters considered as horizontal whitespaces by319// FileCheck input canonicalization.320constexpr StringLiteral SpaceChars = " \t";321322// Parsing helper function that strips the first character in S and returns it.323static char popFront(StringRef &S) {324char C = S.front();325S = S.drop_front();326return C;327}328329char OverflowError::ID = 0;330char UndefVarError::ID = 0;331char ErrorDiagnostic::ID = 0;332char NotFoundError::ID = 0;333char ErrorReported::ID = 0;334335Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(336StringRef &Expr, FileCheckPatternContext *Context,337std::optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,338const SourceMgr &SM) {339Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);340if (!ParseVarResult)341return ParseVarResult.takeError();342StringRef Name = ParseVarResult->Name;343344if (ParseVarResult->IsPseudo)345return ErrorDiagnostic::get(346SM, Name, "definition of pseudo numeric variable unsupported");347348// Detect collisions between string and numeric variables when the latter349// is created later than the former.350if (Context->DefinedVariableTable.contains(Name))351return ErrorDiagnostic::get(352SM, Name, "string variable with name '" + Name + "' already exists");353354Expr = Expr.ltrim(SpaceChars);355if (!Expr.empty())356return ErrorDiagnostic::get(357SM, Expr, "unexpected characters after numeric variable name");358359NumericVariable *DefinedNumericVariable;360auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);361if (VarTableIter != Context->GlobalNumericVariableTable.end()) {362DefinedNumericVariable = VarTableIter->second;363if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat)364return ErrorDiagnostic::get(365SM, Expr, "format different from previous variable definition");366} else367DefinedNumericVariable =368Context->makeNumericVariable(Name, ImplicitFormat, LineNumber);369370return DefinedNumericVariable;371}372373Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(374StringRef Name, bool IsPseudo, std::optional<size_t> LineNumber,375FileCheckPatternContext *Context, const SourceMgr &SM) {376if (IsPseudo && Name != "@LINE")377return ErrorDiagnostic::get(378SM, Name, "invalid pseudo numeric variable '" + Name + "'");379380// Numeric variable definitions and uses are parsed in the order in which381// they appear in the CHECK patterns. For each definition, the pointer to the382// class instance of the corresponding numeric variable definition is stored383// in GlobalNumericVariableTable in parsePattern. Therefore, if the pointer384// we get below is null, it means no such variable was defined before. When385// that happens, we create a dummy variable so that parsing can continue. All386// uses of undefined variables, whether string or numeric, are then diagnosed387// in printNoMatch() after failing to match.388auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);389NumericVariable *NumericVariable;390if (VarTableIter != Context->GlobalNumericVariableTable.end())391NumericVariable = VarTableIter->second;392else {393NumericVariable = Context->makeNumericVariable(394Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned));395Context->GlobalNumericVariableTable[Name] = NumericVariable;396}397398std::optional<size_t> DefLineNumber = NumericVariable->getDefLineNumber();399if (DefLineNumber && LineNumber && *DefLineNumber == *LineNumber)400return ErrorDiagnostic::get(401SM, Name,402"numeric variable '" + Name +403"' defined earlier in the same CHECK directive");404405return std::make_unique<NumericVariableUse>(Name, NumericVariable);406}407408Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(409StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint,410std::optional<size_t> LineNumber, FileCheckPatternContext *Context,411const SourceMgr &SM) {412if (Expr.starts_with("(")) {413if (AO != AllowedOperand::Any)414return ErrorDiagnostic::get(415SM, Expr, "parenthesized expression not permitted here");416return parseParenExpr(Expr, LineNumber, Context, SM);417}418419if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {420// Try to parse as a numeric variable use.421Expected<Pattern::VariableProperties> ParseVarResult =422parseVariable(Expr, SM);423if (ParseVarResult) {424// Try to parse a function call.425if (Expr.ltrim(SpaceChars).starts_with("(")) {426if (AO != AllowedOperand::Any)427return ErrorDiagnostic::get(SM, ParseVarResult->Name,428"unexpected function call");429430return parseCallExpr(Expr, ParseVarResult->Name, LineNumber, Context,431SM);432}433434return parseNumericVariableUse(ParseVarResult->Name,435ParseVarResult->IsPseudo, LineNumber,436Context, SM);437}438439if (AO == AllowedOperand::LineVar)440return ParseVarResult.takeError();441// Ignore the error and retry parsing as a literal.442consumeError(ParseVarResult.takeError());443}444445// Otherwise, parse it as a literal.446APInt LiteralValue;447StringRef SaveExpr = Expr;448bool Negative = Expr.consume_front("-");449if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,450LiteralValue)) {451LiteralValue = toSigned(LiteralValue, Negative);452return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()),453LiteralValue);454}455return ErrorDiagnostic::get(456SM, SaveExpr,457Twine("invalid ") +458(MaybeInvalidConstraint ? "matching constraint or " : "") +459"operand format");460}461462Expected<std::unique_ptr<ExpressionAST>>463Pattern::parseParenExpr(StringRef &Expr, std::optional<size_t> LineNumber,464FileCheckPatternContext *Context, const SourceMgr &SM) {465Expr = Expr.ltrim(SpaceChars);466assert(Expr.starts_with("("));467468// Parse right operand.469Expr.consume_front("(");470Expr = Expr.ltrim(SpaceChars);471if (Expr.empty())472return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");473474// Note: parseNumericOperand handles nested opening parentheses.475Expected<std::unique_ptr<ExpressionAST>> SubExprResult = parseNumericOperand(476Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,477Context, SM);478Expr = Expr.ltrim(SpaceChars);479while (SubExprResult && !Expr.empty() && !Expr.starts_with(")")) {480StringRef OrigExpr = Expr;481SubExprResult = parseBinop(OrigExpr, Expr, std::move(*SubExprResult), false,482LineNumber, Context, SM);483Expr = Expr.ltrim(SpaceChars);484}485if (!SubExprResult)486return SubExprResult;487488if (!Expr.consume_front(")")) {489return ErrorDiagnostic::get(SM, Expr,490"missing ')' at end of nested expression");491}492return SubExprResult;493}494495Expected<std::unique_ptr<ExpressionAST>>496Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,497std::unique_ptr<ExpressionAST> LeftOp,498bool IsLegacyLineExpr, std::optional<size_t> LineNumber,499FileCheckPatternContext *Context, const SourceMgr &SM) {500RemainingExpr = RemainingExpr.ltrim(SpaceChars);501if (RemainingExpr.empty())502return std::move(LeftOp);503504// Check if this is a supported operation and select a function to perform505// it.506SMLoc OpLoc = SMLoc::getFromPointer(RemainingExpr.data());507char Operator = popFront(RemainingExpr);508binop_eval_t EvalBinop;509switch (Operator) {510case '+':511EvalBinop = exprAdd;512break;513case '-':514EvalBinop = exprSub;515break;516default:517return ErrorDiagnostic::get(518SM, OpLoc, Twine("unsupported operation '") + Twine(Operator) + "'");519}520521// Parse right operand.522RemainingExpr = RemainingExpr.ltrim(SpaceChars);523if (RemainingExpr.empty())524return ErrorDiagnostic::get(SM, RemainingExpr,525"missing operand in expression");526// The second operand in a legacy @LINE expression is always a literal.527AllowedOperand AO =528IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;529Expected<std::unique_ptr<ExpressionAST>> RightOpResult =530parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false,531LineNumber, Context, SM);532if (!RightOpResult)533return RightOpResult;534535Expr = Expr.drop_back(RemainingExpr.size());536return std::make_unique<BinaryOperation>(Expr, EvalBinop, std::move(LeftOp),537std::move(*RightOpResult));538}539540Expected<std::unique_ptr<ExpressionAST>>541Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName,542std::optional<size_t> LineNumber,543FileCheckPatternContext *Context, const SourceMgr &SM) {544Expr = Expr.ltrim(SpaceChars);545assert(Expr.starts_with("("));546547auto OptFunc = StringSwitch<binop_eval_t>(FuncName)548.Case("add", exprAdd)549.Case("div", exprDiv)550.Case("max", exprMax)551.Case("min", exprMin)552.Case("mul", exprMul)553.Case("sub", exprSub)554.Default(nullptr);555556if (!OptFunc)557return ErrorDiagnostic::get(558SM, FuncName, Twine("call to undefined function '") + FuncName + "'");559560Expr.consume_front("(");561Expr = Expr.ltrim(SpaceChars);562563// Parse call arguments, which are comma separated.564SmallVector<std::unique_ptr<ExpressionAST>, 4> Args;565while (!Expr.empty() && !Expr.starts_with(")")) {566if (Expr.starts_with(","))567return ErrorDiagnostic::get(SM, Expr, "missing argument");568569// Parse the argument, which is an arbitary expression.570StringRef OuterBinOpExpr = Expr;571Expected<std::unique_ptr<ExpressionAST>> Arg = parseNumericOperand(572Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,573Context, SM);574while (Arg && !Expr.empty()) {575Expr = Expr.ltrim(SpaceChars);576// Have we reached an argument terminator?577if (Expr.starts_with(",") || Expr.starts_with(")"))578break;579580// Arg = Arg <op> <expr>581Arg = parseBinop(OuterBinOpExpr, Expr, std::move(*Arg), false, LineNumber,582Context, SM);583}584585// Prefer an expression error over a generic invalid argument message.586if (!Arg)587return Arg.takeError();588Args.push_back(std::move(*Arg));589590// Have we parsed all available arguments?591Expr = Expr.ltrim(SpaceChars);592if (!Expr.consume_front(","))593break;594595Expr = Expr.ltrim(SpaceChars);596if (Expr.starts_with(")"))597return ErrorDiagnostic::get(SM, Expr, "missing argument");598}599600if (!Expr.consume_front(")"))601return ErrorDiagnostic::get(SM, Expr,602"missing ')' at end of call expression");603604const unsigned NumArgs = Args.size();605if (NumArgs == 2)606return std::make_unique<BinaryOperation>(Expr, *OptFunc, std::move(Args[0]),607std::move(Args[1]));608609// TODO: Support more than binop_eval_t.610return ErrorDiagnostic::get(SM, FuncName,611Twine("function '") + FuncName +612Twine("' takes 2 arguments but ") +613Twine(NumArgs) + " given");614}615616Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(617StringRef Expr, std::optional<NumericVariable *> &DefinedNumericVariable,618bool IsLegacyLineExpr, std::optional<size_t> LineNumber,619FileCheckPatternContext *Context, const SourceMgr &SM) {620std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr;621StringRef DefExpr = StringRef();622DefinedNumericVariable = std::nullopt;623ExpressionFormat ExplicitFormat = ExpressionFormat();624unsigned Precision = 0;625626// Parse format specifier (NOTE: ',' is also an argument separator).627size_t FormatSpecEnd = Expr.find(',');628size_t FunctionStart = Expr.find('(');629if (FormatSpecEnd != StringRef::npos && FormatSpecEnd < FunctionStart) {630StringRef FormatExpr = Expr.take_front(FormatSpecEnd);631Expr = Expr.drop_front(FormatSpecEnd + 1);632FormatExpr = FormatExpr.trim(SpaceChars);633if (!FormatExpr.consume_front("%"))634return ErrorDiagnostic::get(635SM, FormatExpr,636"invalid matching format specification in expression");637638// Parse alternate form flag.639SMLoc AlternateFormFlagLoc = SMLoc::getFromPointer(FormatExpr.data());640bool AlternateForm = FormatExpr.consume_front("#");641642// Parse precision.643if (FormatExpr.consume_front(".")) {644if (FormatExpr.consumeInteger(10, Precision))645return ErrorDiagnostic::get(SM, FormatExpr,646"invalid precision in format specifier");647}648649if (!FormatExpr.empty()) {650// Check for unknown matching format specifier and set matching format in651// class instance representing this expression.652SMLoc FmtLoc = SMLoc::getFromPointer(FormatExpr.data());653switch (popFront(FormatExpr)) {654case 'u':655ExplicitFormat =656ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision);657break;658case 'd':659ExplicitFormat =660ExpressionFormat(ExpressionFormat::Kind::Signed, Precision);661break;662case 'x':663ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower,664Precision, AlternateForm);665break;666case 'X':667ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper,668Precision, AlternateForm);669break;670default:671return ErrorDiagnostic::get(SM, FmtLoc,672"invalid format specifier in expression");673}674}675676if (AlternateForm && ExplicitFormat != ExpressionFormat::Kind::HexLower &&677ExplicitFormat != ExpressionFormat::Kind::HexUpper)678return ErrorDiagnostic::get(679SM, AlternateFormFlagLoc,680"alternate form only supported for hex values");681682FormatExpr = FormatExpr.ltrim(SpaceChars);683if (!FormatExpr.empty())684return ErrorDiagnostic::get(685SM, FormatExpr,686"invalid matching format specification in expression");687}688689// Save variable definition expression if any.690size_t DefEnd = Expr.find(':');691if (DefEnd != StringRef::npos) {692DefExpr = Expr.substr(0, DefEnd);693Expr = Expr.substr(DefEnd + 1);694}695696// Parse matching constraint.697Expr = Expr.ltrim(SpaceChars);698bool HasParsedValidConstraint = Expr.consume_front("==");699700// Parse the expression itself.701Expr = Expr.ltrim(SpaceChars);702if (Expr.empty()) {703if (HasParsedValidConstraint)704return ErrorDiagnostic::get(705SM, Expr, "empty numeric expression should not have a constraint");706} else {707Expr = Expr.rtrim(SpaceChars);708StringRef OuterBinOpExpr = Expr;709// The first operand in a legacy @LINE expression is always the @LINE710// pseudo variable.711AllowedOperand AO =712IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;713Expected<std::unique_ptr<ExpressionAST>> ParseResult = parseNumericOperand(714Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM);715while (ParseResult && !Expr.empty()) {716ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult),717IsLegacyLineExpr, LineNumber, Context, SM);718// Legacy @LINE expressions only allow 2 operands.719if (ParseResult && IsLegacyLineExpr && !Expr.empty())720return ErrorDiagnostic::get(721SM, Expr,722"unexpected characters at end of expression '" + Expr + "'");723}724if (!ParseResult)725return ParseResult.takeError();726ExpressionASTPointer = std::move(*ParseResult);727}728729// Select format of the expression, i.e. (i) its explicit format, if any,730// otherwise (ii) its implicit format, if any, otherwise (iii) the default731// format (unsigned). Error out in case of conflicting implicit format732// without explicit format.733ExpressionFormat Format;734if (ExplicitFormat)735Format = ExplicitFormat;736else if (ExpressionASTPointer) {737Expected<ExpressionFormat> ImplicitFormat =738ExpressionASTPointer->getImplicitFormat(SM);739if (!ImplicitFormat)740return ImplicitFormat.takeError();741Format = *ImplicitFormat;742}743if (!Format)744Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned, Precision);745746std::unique_ptr<Expression> ExpressionPointer =747std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);748749// Parse the numeric variable definition.750if (DefEnd != StringRef::npos) {751DefExpr = DefExpr.ltrim(SpaceChars);752Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition(753DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM);754755if (!ParseResult)756return ParseResult.takeError();757DefinedNumericVariable = *ParseResult;758}759760return std::move(ExpressionPointer);761}762763bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,764SourceMgr &SM, const FileCheckRequest &Req) {765bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;766IgnoreCase = Req.IgnoreCase;767768PatternLoc = SMLoc::getFromPointer(PatternStr.data());769770if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))771// Ignore trailing whitespace.772PatternStr = PatternStr.rtrim(" \t");773774// Check that there is something on the line.775if (PatternStr.empty() && CheckTy != Check::CheckEmpty) {776SM.PrintMessage(PatternLoc, SourceMgr::DK_Error,777"found empty check string with prefix '" + Prefix + ":'");778return true;779}780781if (!PatternStr.empty() && CheckTy == Check::CheckEmpty) {782SM.PrintMessage(783PatternLoc, SourceMgr::DK_Error,784"found non-empty check string for empty check with prefix '" + Prefix +785":'");786return true;787}788789if (CheckTy == Check::CheckEmpty) {790RegExStr = "(\n$)";791return false;792}793794// If literal check, set fixed string.795if (CheckTy.isLiteralMatch()) {796FixedStr = PatternStr;797return false;798}799800// Check to see if this is a fixed string, or if it has regex pieces.801if (!MatchFullLinesHere &&802(PatternStr.size() < 2 ||803(!PatternStr.contains("{{") && !PatternStr.contains("[[")))) {804FixedStr = PatternStr;805return false;806}807808if (MatchFullLinesHere) {809RegExStr += '^';810if (!Req.NoCanonicalizeWhiteSpace)811RegExStr += " *";812}813814// Paren value #0 is for the fully matched string. Any new parenthesized815// values add from there.816unsigned CurParen = 1;817818// Otherwise, there is at least one regex piece. Build up the regex pattern819// by escaping scary characters in fixed strings, building up one big regex.820while (!PatternStr.empty()) {821// RegEx matches.822if (PatternStr.starts_with("{{")) {823// This is the start of a regex match. Scan for the }}.824size_t End = PatternStr.find("}}");825if (End == StringRef::npos) {826SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),827SourceMgr::DK_Error,828"found start of regex string with no end '}}'");829return true;830}831832// Enclose {{}} patterns in parens just like [[]] even though we're not833// capturing the result for any purpose. This is required in case the834// expression contains an alternation like: CHECK: abc{{x|z}}def. We835// want this to turn into: "abc(x|z)def" not "abcx|zdef".836bool HasAlternation = PatternStr.contains('|');837if (HasAlternation) {838RegExStr += '(';839++CurParen;840}841842if (AddRegExToRegEx(PatternStr.substr(2, End - 2), CurParen, SM))843return true;844if (HasAlternation)845RegExStr += ')';846847PatternStr = PatternStr.substr(End + 2);848continue;849}850851// String and numeric substitution blocks. Pattern substitution blocks come852// in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some853// other regex) and assigns it to the string variable 'foo'. The latter854// substitutes foo's value. Numeric substitution blocks recognize the same855// form as string ones, but start with a '#' sign after the double856// brackets. They also accept a combined form which sets a numeric variable857// to the evaluation of an expression. Both string and numeric variable858// names must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*" to be859// valid, as this helps catch some common errors. If there are extra '['s860// before the "[[", treat them literally.861if (PatternStr.starts_with("[[") && !PatternStr.starts_with("[[[")) {862StringRef UnparsedPatternStr = PatternStr.substr(2);863// Find the closing bracket pair ending the match. End is going to be an864// offset relative to the beginning of the match string.865size_t End = FindRegexVarEnd(UnparsedPatternStr, SM);866StringRef MatchStr = UnparsedPatternStr.substr(0, End);867bool IsNumBlock = MatchStr.consume_front("#");868869if (End == StringRef::npos) {870SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()),871SourceMgr::DK_Error,872"Invalid substitution block, no ]] found");873return true;874}875// Strip the substitution block we are parsing. End points to the start876// of the "]]" closing the expression so account for it in computing the877// index of the first unparsed character.878PatternStr = UnparsedPatternStr.substr(End + 2);879880bool IsDefinition = false;881bool SubstNeeded = false;882// Whether the substitution block is a legacy use of @LINE with string883// substitution block syntax.884bool IsLegacyLineExpr = false;885StringRef DefName;886StringRef SubstStr;887StringRef MatchRegexp;888std::string WildcardRegexp;889size_t SubstInsertIdx = RegExStr.size();890891// Parse string variable or legacy @LINE expression.892if (!IsNumBlock) {893size_t VarEndIdx = MatchStr.find(':');894size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t");895if (SpacePos != StringRef::npos) {896SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos),897SourceMgr::DK_Error, "unexpected whitespace");898return true;899}900901// Get the name (e.g. "foo") and verify it is well formed.902StringRef OrigMatchStr = MatchStr;903Expected<Pattern::VariableProperties> ParseVarResult =904parseVariable(MatchStr, SM);905if (!ParseVarResult) {906logAllUnhandledErrors(ParseVarResult.takeError(), errs());907return true;908}909StringRef Name = ParseVarResult->Name;910bool IsPseudo = ParseVarResult->IsPseudo;911912IsDefinition = (VarEndIdx != StringRef::npos);913SubstNeeded = !IsDefinition;914if (IsDefinition) {915if ((IsPseudo || !MatchStr.consume_front(":"))) {916SM.PrintMessage(SMLoc::getFromPointer(Name.data()),917SourceMgr::DK_Error,918"invalid name in string variable definition");919return true;920}921922// Detect collisions between string and numeric variables when the923// former is created later than the latter.924if (Context->GlobalNumericVariableTable.contains(Name)) {925SM.PrintMessage(926SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,927"numeric variable with name '" + Name + "' already exists");928return true;929}930DefName = Name;931MatchRegexp = MatchStr;932} else {933if (IsPseudo) {934MatchStr = OrigMatchStr;935IsLegacyLineExpr = IsNumBlock = true;936} else {937if (!MatchStr.empty()) {938SM.PrintMessage(SMLoc::getFromPointer(Name.data()),939SourceMgr::DK_Error,940"invalid name in string variable use");941return true;942}943SubstStr = Name;944}945}946}947948// Parse numeric substitution block.949std::unique_ptr<Expression> ExpressionPointer;950std::optional<NumericVariable *> DefinedNumericVariable;951if (IsNumBlock) {952Expected<std::unique_ptr<Expression>> ParseResult =953parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,954IsLegacyLineExpr, LineNumber, Context,955SM);956if (!ParseResult) {957logAllUnhandledErrors(ParseResult.takeError(), errs());958return true;959}960ExpressionPointer = std::move(*ParseResult);961SubstNeeded = ExpressionPointer->getAST() != nullptr;962if (DefinedNumericVariable) {963IsDefinition = true;964DefName = (*DefinedNumericVariable)->getName();965}966if (SubstNeeded)967SubstStr = MatchStr;968else {969ExpressionFormat Format = ExpressionPointer->getFormat();970WildcardRegexp = cantFail(Format.getWildcardRegex());971MatchRegexp = WildcardRegexp;972}973}974975// Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].976if (IsDefinition) {977RegExStr += '(';978++SubstInsertIdx;979980if (IsNumBlock) {981NumericVariableMatch NumericVariableDefinition = {982*DefinedNumericVariable, CurParen};983NumericVariableDefs[DefName] = NumericVariableDefinition;984// This store is done here rather than in match() to allow985// parseNumericVariableUse() to get the pointer to the class instance986// of the right variable definition corresponding to a given numeric987// variable use.988Context->GlobalNumericVariableTable[DefName] =989*DefinedNumericVariable;990} else {991VariableDefs[DefName] = CurParen;992// Mark string variable as defined to detect collisions between993// string and numeric variables in parseNumericVariableUse() and994// defineCmdlineVariables() when the latter is created later than the995// former. We cannot reuse GlobalVariableTable for this by populating996// it with an empty string since we would then lose the ability to997// detect the use of an undefined variable in match().998Context->DefinedVariableTable[DefName] = true;999}10001001++CurParen;1002}10031004if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM))1005return true;10061007if (IsDefinition)1008RegExStr += ')';10091010// Handle substitutions: [[foo]] and [[#<foo expr>]].1011if (SubstNeeded) {1012// Handle substitution of string variables that were defined earlier on1013// the same line by emitting a backreference. Expressions do not1014// support substituting a numeric variable defined on the same line.1015if (!IsNumBlock && VariableDefs.find(SubstStr) != VariableDefs.end()) {1016unsigned CaptureParenGroup = VariableDefs[SubstStr];1017if (CaptureParenGroup < 1 || CaptureParenGroup > 9) {1018SM.PrintMessage(SMLoc::getFromPointer(SubstStr.data()),1019SourceMgr::DK_Error,1020"Can't back-reference more than 9 variables");1021return true;1022}1023AddBackrefToRegEx(CaptureParenGroup);1024} else {1025// Handle substitution of string variables ([[<var>]]) defined in1026// previous CHECK patterns, and substitution of expressions.1027Substitution *Substitution =1028IsNumBlock1029? Context->makeNumericSubstitution(1030SubstStr, std::move(ExpressionPointer), SubstInsertIdx)1031: Context->makeStringSubstitution(SubstStr, SubstInsertIdx);1032Substitutions.push_back(Substitution);1033}1034}10351036continue;1037}10381039// Handle fixed string matches.1040// Find the end, which is the start of the next regex.1041size_t FixedMatchEnd =1042std::min(PatternStr.find("{{", 1), PatternStr.find("[[", 1));1043RegExStr += Regex::escape(PatternStr.substr(0, FixedMatchEnd));1044PatternStr = PatternStr.substr(FixedMatchEnd);1045}10461047if (MatchFullLinesHere) {1048if (!Req.NoCanonicalizeWhiteSpace)1049RegExStr += " *";1050RegExStr += '$';1051}10521053return false;1054}10551056bool Pattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM) {1057Regex R(RS);1058std::string Error;1059if (!R.isValid(Error)) {1060SM.PrintMessage(SMLoc::getFromPointer(RS.data()), SourceMgr::DK_Error,1061"invalid regex: " + Error);1062return true;1063}10641065RegExStr += RS.str();1066CurParen += R.getNumMatches();1067return false;1068}10691070void Pattern::AddBackrefToRegEx(unsigned BackrefNum) {1071assert(BackrefNum >= 1 && BackrefNum <= 9 && "Invalid backref number");1072std::string Backref = std::string("\\") + std::string(1, '0' + BackrefNum);1073RegExStr += Backref;1074}10751076Pattern::MatchResult Pattern::match(StringRef Buffer,1077const SourceMgr &SM) const {1078// If this is the EOF pattern, match it immediately.1079if (CheckTy == Check::CheckEOF)1080return MatchResult(Buffer.size(), 0, Error::success());10811082// If this is a fixed string pattern, just match it now.1083if (!FixedStr.empty()) {1084size_t Pos =1085IgnoreCase ? Buffer.find_insensitive(FixedStr) : Buffer.find(FixedStr);1086if (Pos == StringRef::npos)1087return make_error<NotFoundError>();1088return MatchResult(Pos, /*MatchLen=*/FixedStr.size(), Error::success());1089}10901091// Regex match.10921093// If there are substitutions, we need to create a temporary string with the1094// actual value.1095StringRef RegExToMatch = RegExStr;1096std::string TmpStr;1097if (!Substitutions.empty()) {1098TmpStr = RegExStr;1099if (LineNumber)1100Context->LineVariable->setValue(1101APInt(sizeof(*LineNumber) * 8, *LineNumber));11021103size_t InsertOffset = 0;1104// Substitute all string variables and expressions whose values are only1105// now known. Use of string variables defined on the same line are handled1106// by back-references.1107Error Errs = Error::success();1108for (const auto &Substitution : Substitutions) {1109// Substitute and check for failure (e.g. use of undefined variable).1110Expected<std::string> Value = Substitution->getResult();1111if (!Value) {1112// Convert to an ErrorDiagnostic to get location information. This is1113// done here rather than printMatch/printNoMatch since now we know which1114// substitution block caused the overflow.1115Errs = joinErrors(std::move(Errs),1116handleErrors(1117Value.takeError(),1118[&](const OverflowError &E) {1119return ErrorDiagnostic::get(1120SM, Substitution->getFromString(),1121"unable to substitute variable or "1122"numeric expression: overflow error");1123},1124[&SM](const UndefVarError &E) {1125return ErrorDiagnostic::get(SM, E.getVarName(),1126E.message());1127}));1128continue;1129}11301131// Plop it into the regex at the adjusted offset.1132TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset,1133Value->begin(), Value->end());1134InsertOffset += Value->size();1135}1136if (Errs)1137return std::move(Errs);11381139// Match the newly constructed regex.1140RegExToMatch = TmpStr;1141}11421143SmallVector<StringRef, 4> MatchInfo;1144unsigned int Flags = Regex::Newline;1145if (IgnoreCase)1146Flags |= Regex::IgnoreCase;1147if (!Regex(RegExToMatch, Flags).match(Buffer, &MatchInfo))1148return make_error<NotFoundError>();11491150// Successful regex match.1151assert(!MatchInfo.empty() && "Didn't get any match");1152StringRef FullMatch = MatchInfo[0];11531154// If this defines any string variables, remember their values.1155for (const auto &VariableDef : VariableDefs) {1156assert(VariableDef.second < MatchInfo.size() && "Internal paren error");1157Context->GlobalVariableTable[VariableDef.first] =1158MatchInfo[VariableDef.second];1159}11601161// Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after1162// the required preceding newline, which is consumed by the pattern in the1163// case of CHECK-EMPTY but not CHECK-NEXT.1164size_t MatchStartSkip = CheckTy == Check::CheckEmpty;1165Match TheMatch;1166TheMatch.Pos = FullMatch.data() - Buffer.data() + MatchStartSkip;1167TheMatch.Len = FullMatch.size() - MatchStartSkip;11681169// If this defines any numeric variables, remember their values.1170for (const auto &NumericVariableDef : NumericVariableDefs) {1171const NumericVariableMatch &NumericVariableMatch =1172NumericVariableDef.getValue();1173unsigned CaptureParenGroup = NumericVariableMatch.CaptureParenGroup;1174assert(CaptureParenGroup < MatchInfo.size() && "Internal paren error");1175NumericVariable *DefinedNumericVariable =1176NumericVariableMatch.DefinedNumericVariable;11771178StringRef MatchedValue = MatchInfo[CaptureParenGroup];1179ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat();1180APInt Value = Format.valueFromStringRepr(MatchedValue, SM);1181DefinedNumericVariable->setValue(Value, MatchedValue);1182}11831184return MatchResult(TheMatch, Error::success());1185}11861187unsigned Pattern::computeMatchDistance(StringRef Buffer) const {1188// Just compute the number of matching characters. For regular expressions, we1189// just compare against the regex itself and hope for the best.1190//1191// FIXME: One easy improvement here is have the regex lib generate a single1192// example regular expression which matches, and use that as the example1193// string.1194StringRef ExampleString(FixedStr);1195if (ExampleString.empty())1196ExampleString = RegExStr;11971198// Only compare up to the first line in the buffer, or the string size.1199StringRef BufferPrefix = Buffer.substr(0, ExampleString.size());1200BufferPrefix = BufferPrefix.split('\n').first;1201return BufferPrefix.edit_distance(ExampleString);1202}12031204void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,1205SMRange Range,1206FileCheckDiag::MatchType MatchTy,1207std::vector<FileCheckDiag> *Diags) const {1208// Print what we know about substitutions.1209if (!Substitutions.empty()) {1210for (const auto &Substitution : Substitutions) {1211SmallString<256> Msg;1212raw_svector_ostream OS(Msg);12131214Expected<std::string> MatchedValue = Substitution->getResult();1215// Substitution failures are handled in printNoMatch().1216if (!MatchedValue) {1217consumeError(MatchedValue.takeError());1218continue;1219}12201221OS << "with \"";1222OS.write_escaped(Substitution->getFromString()) << "\" equal to \"";1223OS.write_escaped(*MatchedValue) << "\"";12241225// We report only the start of the match/search range to suggest we are1226// reporting the substitutions as set at the start of the match/search.1227// Indicating a non-zero-length range might instead seem to imply that the1228// substitution matches or was captured from exactly that range.1229if (Diags)1230Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy,1231SMRange(Range.Start, Range.Start), OS.str());1232else1233SM.PrintMessage(Range.Start, SourceMgr::DK_Note, OS.str());1234}1235}1236}12371238void Pattern::printVariableDefs(const SourceMgr &SM,1239FileCheckDiag::MatchType MatchTy,1240std::vector<FileCheckDiag> *Diags) const {1241if (VariableDefs.empty() && NumericVariableDefs.empty())1242return;1243// Build list of variable captures.1244struct VarCapture {1245StringRef Name;1246SMRange Range;1247};1248SmallVector<VarCapture, 2> VarCaptures;1249for (const auto &VariableDef : VariableDefs) {1250VarCapture VC;1251VC.Name = VariableDef.first;1252StringRef Value = Context->GlobalVariableTable[VC.Name];1253SMLoc Start = SMLoc::getFromPointer(Value.data());1254SMLoc End = SMLoc::getFromPointer(Value.data() + Value.size());1255VC.Range = SMRange(Start, End);1256VarCaptures.push_back(VC);1257}1258for (const auto &VariableDef : NumericVariableDefs) {1259VarCapture VC;1260VC.Name = VariableDef.getKey();1261std::optional<StringRef> StrValue =1262VariableDef.getValue().DefinedNumericVariable->getStringValue();1263if (!StrValue)1264continue;1265SMLoc Start = SMLoc::getFromPointer(StrValue->data());1266SMLoc End = SMLoc::getFromPointer(StrValue->data() + StrValue->size());1267VC.Range = SMRange(Start, End);1268VarCaptures.push_back(VC);1269}1270// Sort variable captures by the order in which they matched the input.1271// Ranges shouldn't be overlapping, so we can just compare the start.1272llvm::sort(VarCaptures, [](const VarCapture &A, const VarCapture &B) {1273if (&A == &B)1274return false;1275assert(A.Range.Start != B.Range.Start &&1276"unexpected overlapping variable captures");1277return A.Range.Start.getPointer() < B.Range.Start.getPointer();1278});1279// Create notes for the sorted captures.1280for (const VarCapture &VC : VarCaptures) {1281SmallString<256> Msg;1282raw_svector_ostream OS(Msg);1283OS << "captured var \"" << VC.Name << "\"";1284if (Diags)1285Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy, VC.Range, OS.str());1286else1287SM.PrintMessage(VC.Range.Start, SourceMgr::DK_Note, OS.str(), VC.Range);1288}1289}12901291static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,1292const SourceMgr &SM, SMLoc Loc,1293Check::FileCheckType CheckTy,1294StringRef Buffer, size_t Pos, size_t Len,1295std::vector<FileCheckDiag> *Diags,1296bool AdjustPrevDiags = false) {1297SMLoc Start = SMLoc::getFromPointer(Buffer.data() + Pos);1298SMLoc End = SMLoc::getFromPointer(Buffer.data() + Pos + Len);1299SMRange Range(Start, End);1300if (Diags) {1301if (AdjustPrevDiags) {1302SMLoc CheckLoc = Diags->rbegin()->CheckLoc;1303for (auto I = Diags->rbegin(), E = Diags->rend();1304I != E && I->CheckLoc == CheckLoc; ++I)1305I->MatchTy = MatchTy;1306} else1307Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range);1308}1309return Range;1310}13111312void Pattern::printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,1313std::vector<FileCheckDiag> *Diags) const {1314// Attempt to find the closest/best fuzzy match. Usually an error happens1315// because some string in the output didn't exactly match. In these cases, we1316// would like to show the user a best guess at what "should have" matched, to1317// save them having to actually check the input manually.1318size_t NumLinesForward = 0;1319size_t Best = StringRef::npos;1320double BestQuality = 0;13211322// Use an arbitrary 4k limit on how far we will search.1323for (size_t i = 0, e = std::min(size_t(4096), Buffer.size()); i != e; ++i) {1324if (Buffer[i] == '\n')1325++NumLinesForward;13261327// Patterns have leading whitespace stripped, so skip whitespace when1328// looking for something which looks like a pattern.1329if (Buffer[i] == ' ' || Buffer[i] == '\t')1330continue;13311332// Compute the "quality" of this match as an arbitrary combination of the1333// match distance and the number of lines skipped to get to this match.1334unsigned Distance = computeMatchDistance(Buffer.substr(i));1335double Quality = Distance + (NumLinesForward / 100.);13361337if (Quality < BestQuality || Best == StringRef::npos) {1338Best = i;1339BestQuality = Quality;1340}1341}13421343// Print the "possible intended match here" line if we found something1344// reasonable and not equal to what we showed in the "scanning from here"1345// line.1346if (Best && Best != StringRef::npos && BestQuality < 50) {1347SMRange MatchRange =1348ProcessMatchResult(FileCheckDiag::MatchFuzzy, SM, getLoc(),1349getCheckTy(), Buffer, Best, 0, Diags);1350SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note,1351"possible intended match here");13521353// FIXME: If we wanted to be really friendly we would show why the match1354// failed, as it can be hard to spot simple one character differences.1355}1356}13571358Expected<StringRef>1359FileCheckPatternContext::getPatternVarValue(StringRef VarName) {1360auto VarIter = GlobalVariableTable.find(VarName);1361if (VarIter == GlobalVariableTable.end())1362return make_error<UndefVarError>(VarName);13631364return VarIter->second;1365}13661367template <class... Types>1368NumericVariable *FileCheckPatternContext::makeNumericVariable(Types... args) {1369NumericVariables.push_back(std::make_unique<NumericVariable>(args...));1370return NumericVariables.back().get();1371}13721373Substitution *1374FileCheckPatternContext::makeStringSubstitution(StringRef VarName,1375size_t InsertIdx) {1376Substitutions.push_back(1377std::make_unique<StringSubstitution>(this, VarName, InsertIdx));1378return Substitutions.back().get();1379}13801381Substitution *FileCheckPatternContext::makeNumericSubstitution(1382StringRef ExpressionStr, std::unique_ptr<Expression> Expression,1383size_t InsertIdx) {1384Substitutions.push_back(std::make_unique<NumericSubstitution>(1385this, ExpressionStr, std::move(Expression), InsertIdx));1386return Substitutions.back().get();1387}13881389size_t Pattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {1390// Offset keeps track of the current offset within the input Str1391size_t Offset = 0;1392// [...] Nesting depth1393size_t BracketDepth = 0;13941395while (!Str.empty()) {1396if (Str.starts_with("]]") && BracketDepth == 0)1397return Offset;1398if (Str[0] == '\\') {1399// Backslash escapes the next char within regexes, so skip them both.1400Str = Str.substr(2);1401Offset += 2;1402} else {1403switch (Str[0]) {1404default:1405break;1406case '[':1407BracketDepth++;1408break;1409case ']':1410if (BracketDepth == 0) {1411SM.PrintMessage(SMLoc::getFromPointer(Str.data()),1412SourceMgr::DK_Error,1413"missing closing \"]\" for regex variable");1414exit(1);1415}1416BracketDepth--;1417break;1418}1419Str = Str.substr(1);1420Offset++;1421}1422}14231424return StringRef::npos;1425}14261427StringRef FileCheck::CanonicalizeFile(MemoryBuffer &MB,1428SmallVectorImpl<char> &OutputBuffer) {1429OutputBuffer.reserve(MB.getBufferSize());14301431for (const char *Ptr = MB.getBufferStart(), *End = MB.getBufferEnd();1432Ptr != End; ++Ptr) {1433// Eliminate trailing dosish \r.1434if (Ptr <= End - 2 && Ptr[0] == '\r' && Ptr[1] == '\n') {1435continue;1436}14371438// If current char is not a horizontal whitespace or if horizontal1439// whitespace canonicalization is disabled, dump it to output as is.1440if (Req.NoCanonicalizeWhiteSpace || (*Ptr != ' ' && *Ptr != '\t')) {1441OutputBuffer.push_back(*Ptr);1442continue;1443}14441445// Otherwise, add one space and advance over neighboring space.1446OutputBuffer.push_back(' ');1447while (Ptr + 1 != End && (Ptr[1] == ' ' || Ptr[1] == '\t'))1448++Ptr;1449}14501451// Add a null byte and then return all but that byte.1452OutputBuffer.push_back('\0');1453return StringRef(OutputBuffer.data(), OutputBuffer.size() - 1);1454}14551456FileCheckDiag::FileCheckDiag(const SourceMgr &SM,1457const Check::FileCheckType &CheckTy,1458SMLoc CheckLoc, MatchType MatchTy,1459SMRange InputRange, StringRef Note)1460: CheckTy(CheckTy), CheckLoc(CheckLoc), MatchTy(MatchTy), Note(Note) {1461auto Start = SM.getLineAndColumn(InputRange.Start);1462auto End = SM.getLineAndColumn(InputRange.End);1463InputStartLine = Start.first;1464InputStartCol = Start.second;1465InputEndLine = End.first;1466InputEndCol = End.second;1467}14681469static bool IsPartOfWord(char c) {1470return (isAlnum(c) || c == '-' || c == '_');1471}14721473Check::FileCheckType &Check::FileCheckType::setCount(int C) {1474assert(Count > 0 && "zero and negative counts are not supported");1475assert((C == 1 || Kind == CheckPlain) &&1476"count supported only for plain CHECK directives");1477Count = C;1478return *this;1479}14801481std::string Check::FileCheckType::getModifiersDescription() const {1482if (Modifiers.none())1483return "";1484std::string Ret;1485raw_string_ostream OS(Ret);1486OS << '{';1487if (isLiteralMatch())1488OS << "LITERAL";1489OS << '}';1490return Ret;1491}14921493std::string Check::FileCheckType::getDescription(StringRef Prefix) const {1494// Append directive modifiers.1495auto WithModifiers = [this, Prefix](StringRef Str) -> std::string {1496return (Prefix + Str + getModifiersDescription()).str();1497};14981499switch (Kind) {1500case Check::CheckNone:1501return "invalid";1502case Check::CheckMisspelled:1503return "misspelled";1504case Check::CheckPlain:1505if (Count > 1)1506return WithModifiers("-COUNT");1507return WithModifiers("");1508case Check::CheckNext:1509return WithModifiers("-NEXT");1510case Check::CheckSame:1511return WithModifiers("-SAME");1512case Check::CheckNot:1513return WithModifiers("-NOT");1514case Check::CheckDAG:1515return WithModifiers("-DAG");1516case Check::CheckLabel:1517return WithModifiers("-LABEL");1518case Check::CheckEmpty:1519return WithModifiers("-EMPTY");1520case Check::CheckComment:1521return std::string(Prefix);1522case Check::CheckEOF:1523return "implicit EOF";1524case Check::CheckBadNot:1525return "bad NOT";1526case Check::CheckBadCount:1527return "bad COUNT";1528}1529llvm_unreachable("unknown FileCheckType");1530}15311532static std::pair<Check::FileCheckType, StringRef>1533FindCheckType(const FileCheckRequest &Req, StringRef Buffer, StringRef Prefix,1534bool &Misspelled) {1535if (Buffer.size() <= Prefix.size())1536return {Check::CheckNone, StringRef()};15371538StringRef Rest = Buffer.drop_front(Prefix.size());1539// Check for comment.1540if (llvm::is_contained(Req.CommentPrefixes, Prefix)) {1541if (Rest.consume_front(":"))1542return {Check::CheckComment, Rest};1543// Ignore a comment prefix if it has a suffix like "-NOT".1544return {Check::CheckNone, StringRef()};1545}15461547auto ConsumeModifiers = [&](Check::FileCheckType Ret)1548-> std::pair<Check::FileCheckType, StringRef> {1549if (Rest.consume_front(":"))1550return {Ret, Rest};1551if (!Rest.consume_front("{"))1552return {Check::CheckNone, StringRef()};15531554// Parse the modifiers, speparated by commas.1555do {1556// Allow whitespace in modifiers list.1557Rest = Rest.ltrim();1558if (Rest.consume_front("LITERAL"))1559Ret.setLiteralMatch();1560else1561return {Check::CheckNone, Rest};1562// Allow whitespace in modifiers list.1563Rest = Rest.ltrim();1564} while (Rest.consume_front(","));1565if (!Rest.consume_front("}:"))1566return {Check::CheckNone, Rest};1567return {Ret, Rest};1568};15691570// Verify that the prefix is followed by directive modifiers or a colon.1571if (Rest.consume_front(":"))1572return {Check::CheckPlain, Rest};1573if (Rest.front() == '{')1574return ConsumeModifiers(Check::CheckPlain);15751576if (Rest.consume_front("_"))1577Misspelled = true;1578else if (!Rest.consume_front("-"))1579return {Check::CheckNone, StringRef()};15801581if (Rest.consume_front("COUNT-")) {1582int64_t Count;1583if (Rest.consumeInteger(10, Count))1584// Error happened in parsing integer.1585return {Check::CheckBadCount, Rest};1586if (Count <= 0 || Count > INT32_MAX)1587return {Check::CheckBadCount, Rest};1588if (Rest.front() != ':' && Rest.front() != '{')1589return {Check::CheckBadCount, Rest};1590return ConsumeModifiers(1591Check::FileCheckType(Check::CheckPlain).setCount(Count));1592}15931594// You can't combine -NOT with another suffix.1595if (Rest.starts_with("DAG-NOT:") || Rest.starts_with("NOT-DAG:") ||1596Rest.starts_with("NEXT-NOT:") || Rest.starts_with("NOT-NEXT:") ||1597Rest.starts_with("SAME-NOT:") || Rest.starts_with("NOT-SAME:") ||1598Rest.starts_with("EMPTY-NOT:") || Rest.starts_with("NOT-EMPTY:"))1599return {Check::CheckBadNot, Rest};16001601if (Rest.consume_front("NEXT"))1602return ConsumeModifiers(Check::CheckNext);16031604if (Rest.consume_front("SAME"))1605return ConsumeModifiers(Check::CheckSame);16061607if (Rest.consume_front("NOT"))1608return ConsumeModifiers(Check::CheckNot);16091610if (Rest.consume_front("DAG"))1611return ConsumeModifiers(Check::CheckDAG);16121613if (Rest.consume_front("LABEL"))1614return ConsumeModifiers(Check::CheckLabel);16151616if (Rest.consume_front("EMPTY"))1617return ConsumeModifiers(Check::CheckEmpty);16181619return {Check::CheckNone, Rest};1620}16211622static std::pair<Check::FileCheckType, StringRef>1623FindCheckType(const FileCheckRequest &Req, StringRef Buffer, StringRef Prefix) {1624bool Misspelled = false;1625auto Res = FindCheckType(Req, Buffer, Prefix, Misspelled);1626if (Res.first != Check::CheckNone && Misspelled)1627return {Check::CheckMisspelled, Res.second};1628return Res;1629}16301631// From the given position, find the next character after the word.1632static size_t SkipWord(StringRef Str, size_t Loc) {1633while (Loc < Str.size() && IsPartOfWord(Str[Loc]))1634++Loc;1635return Loc;1636}16371638static const char *DefaultCheckPrefixes[] = {"CHECK"};1639static const char *DefaultCommentPrefixes[] = {"COM", "RUN"};16401641static void addDefaultPrefixes(FileCheckRequest &Req) {1642if (Req.CheckPrefixes.empty()) {1643for (const char *Prefix : DefaultCheckPrefixes)1644Req.CheckPrefixes.push_back(Prefix);1645Req.IsDefaultCheckPrefix = true;1646}1647if (Req.CommentPrefixes.empty())1648for (const char *Prefix : DefaultCommentPrefixes)1649Req.CommentPrefixes.push_back(Prefix);1650}16511652struct PrefixMatcher {1653/// Prefixes and their first occurrence past the current position.1654SmallVector<std::pair<StringRef, size_t>> Prefixes;1655StringRef Input;16561657PrefixMatcher(ArrayRef<StringRef> CheckPrefixes,1658ArrayRef<StringRef> CommentPrefixes, StringRef Input)1659: Input(Input) {1660for (StringRef Prefix : CheckPrefixes)1661Prefixes.push_back({Prefix, Input.find(Prefix)});1662for (StringRef Prefix : CommentPrefixes)1663Prefixes.push_back({Prefix, Input.find(Prefix)});16641665// Sort by descending length.1666llvm::sort(Prefixes,1667[](auto A, auto B) { return A.first.size() > B.first.size(); });1668}16691670/// Find the next match of a prefix in Buffer.1671/// Returns empty StringRef if not found.1672StringRef match(StringRef Buffer) {1673assert(Buffer.data() >= Input.data() &&1674Buffer.data() + Buffer.size() == Input.data() + Input.size() &&1675"Buffer must be suffix of Input");16761677size_t From = Buffer.data() - Input.data();1678StringRef Match;1679for (auto &[Prefix, Pos] : Prefixes) {1680// If the last occurrence was before From, find the next one after From.1681if (Pos < From)1682Pos = Input.find(Prefix, From);1683// Find the first prefix with the lowest position.1684if (Pos != StringRef::npos &&1685(Match.empty() || size_t(Match.data() - Input.data()) > Pos))1686Match = StringRef(Input.substr(Pos, Prefix.size()));1687}1688return Match;1689}1690};16911692/// Searches the buffer for the first prefix in the prefix regular expression.1693///1694/// This searches the buffer using the provided regular expression, however it1695/// enforces constraints beyond that:1696/// 1) The found prefix must not be a suffix of something that looks like1697/// a valid prefix.1698/// 2) The found prefix must be followed by a valid check type suffix using \c1699/// FindCheckType above.1700///1701/// \returns a pair of StringRefs into the Buffer, which combines:1702/// - the first match of the regular expression to satisfy these two is1703/// returned,1704/// otherwise an empty StringRef is returned to indicate failure.1705/// - buffer rewound to the location right after parsed suffix, for parsing1706/// to continue from1707///1708/// If this routine returns a valid prefix, it will also shrink \p Buffer to1709/// start at the beginning of the returned prefix, increment \p LineNumber for1710/// each new line consumed from \p Buffer, and set \p CheckTy to the type of1711/// check found by examining the suffix.1712///1713/// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy1714/// is unspecified.1715static std::pair<StringRef, StringRef>1716FindFirstMatchingPrefix(const FileCheckRequest &Req, PrefixMatcher &Matcher,1717StringRef &Buffer, unsigned &LineNumber,1718Check::FileCheckType &CheckTy) {1719while (!Buffer.empty()) {1720// Find the first (longest) prefix match.1721StringRef Prefix = Matcher.match(Buffer);1722if (Prefix.empty())1723// No match at all, bail.1724return {StringRef(), StringRef()};17251726assert(Prefix.data() >= Buffer.data() &&1727Prefix.data() < Buffer.data() + Buffer.size() &&1728"Prefix doesn't start inside of buffer!");1729size_t Loc = Prefix.data() - Buffer.data();1730StringRef Skipped = Buffer.substr(0, Loc);1731Buffer = Buffer.drop_front(Loc);1732LineNumber += Skipped.count('\n');17331734// Check that the matched prefix isn't a suffix of some other check-like1735// word.1736// FIXME: This is a very ad-hoc check. it would be better handled in some1737// other way. Among other things it seems hard to distinguish between1738// intentional and unintentional uses of this feature.1739if (Skipped.empty() || !IsPartOfWord(Skipped.back())) {1740// Now extract the type.1741StringRef AfterSuffix;1742std::tie(CheckTy, AfterSuffix) = FindCheckType(Req, Buffer, Prefix);17431744// If we've found a valid check type for this prefix, we're done.1745if (CheckTy != Check::CheckNone)1746return {Prefix, AfterSuffix};1747}17481749// If we didn't successfully find a prefix, we need to skip this invalid1750// prefix and continue scanning. We directly skip the prefix that was1751// matched and any additional parts of that check-like word.1752Buffer = Buffer.drop_front(SkipWord(Buffer, Prefix.size()));1753}17541755// We ran out of buffer while skipping partial matches so give up.1756return {StringRef(), StringRef()};1757}17581759void FileCheckPatternContext::createLineVariable() {1760assert(!LineVariable && "@LINE pseudo numeric variable already created");1761StringRef LineName = "@LINE";1762LineVariable = makeNumericVariable(1763LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned));1764GlobalNumericVariableTable[LineName] = LineVariable;1765}17661767FileCheck::FileCheck(FileCheckRequest Req)1768: Req(Req), PatternContext(std::make_unique<FileCheckPatternContext>()),1769CheckStrings(std::make_unique<std::vector<FileCheckString>>()) {}17701771FileCheck::~FileCheck() = default;17721773bool FileCheck::readCheckFile(1774SourceMgr &SM, StringRef Buffer,1775std::pair<unsigned, unsigned> *ImpPatBufferIDRange) {1776if (ImpPatBufferIDRange)1777ImpPatBufferIDRange->first = ImpPatBufferIDRange->second = 0;17781779Error DefineError =1780PatternContext->defineCmdlineVariables(Req.GlobalDefines, SM);1781if (DefineError) {1782logAllUnhandledErrors(std::move(DefineError), errs());1783return true;1784}17851786PatternContext->createLineVariable();17871788std::vector<FileCheckString::DagNotPrefixInfo> ImplicitNegativeChecks;1789for (StringRef PatternString : Req.ImplicitCheckNot) {1790// Create a buffer with fake command line content in order to display the1791// command line option responsible for the specific implicit CHECK-NOT.1792std::string Prefix = "-implicit-check-not='";1793std::string Suffix = "'";1794std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy(1795(Prefix + PatternString + Suffix).str(), "command line");17961797StringRef PatternInBuffer =1798CmdLine->getBuffer().substr(Prefix.size(), PatternString.size());1799unsigned BufferID = SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());1800if (ImpPatBufferIDRange) {1801if (ImpPatBufferIDRange->first == ImpPatBufferIDRange->second) {1802ImpPatBufferIDRange->first = BufferID;1803ImpPatBufferIDRange->second = BufferID + 1;1804} else {1805assert(BufferID == ImpPatBufferIDRange->second &&1806"expected consecutive source buffer IDs");1807++ImpPatBufferIDRange->second;1808}1809}18101811ImplicitNegativeChecks.emplace_back(1812Pattern(Check::CheckNot, PatternContext.get()),1813StringRef("IMPLICIT-CHECK"));1814ImplicitNegativeChecks.back().DagNotPat.parsePattern(1815PatternInBuffer, "IMPLICIT-CHECK", SM, Req);1816}18171818std::vector<FileCheckString::DagNotPrefixInfo> DagNotMatches =1819ImplicitNegativeChecks;1820// LineNumber keeps track of the line on which CheckPrefix instances are1821// found.1822unsigned LineNumber = 1;18231824addDefaultPrefixes(Req);1825PrefixMatcher Matcher(Req.CheckPrefixes, Req.CommentPrefixes, Buffer);1826std::set<StringRef> PrefixesNotFound(Req.CheckPrefixes.begin(),1827Req.CheckPrefixes.end());1828const size_t DistinctPrefixes = PrefixesNotFound.size();1829while (true) {1830Check::FileCheckType CheckTy;18311832// See if a prefix occurs in the memory buffer.1833StringRef UsedPrefix;1834StringRef AfterSuffix;1835std::tie(UsedPrefix, AfterSuffix) =1836FindFirstMatchingPrefix(Req, Matcher, Buffer, LineNumber, CheckTy);1837if (UsedPrefix.empty())1838break;1839if (CheckTy != Check::CheckComment)1840PrefixesNotFound.erase(UsedPrefix);18411842assert(UsedPrefix.data() == Buffer.data() &&1843"Failed to move Buffer's start forward, or pointed prefix outside "1844"of the buffer!");1845assert(AfterSuffix.data() >= Buffer.data() &&1846AfterSuffix.data() < Buffer.data() + Buffer.size() &&1847"Parsing after suffix doesn't start inside of buffer!");18481849// Location to use for error messages.1850const char *UsedPrefixStart = UsedPrefix.data();18511852// Skip the buffer to the end of parsed suffix (or just prefix, if no good1853// suffix was processed).1854Buffer = AfterSuffix.empty() ? Buffer.drop_front(UsedPrefix.size())1855: AfterSuffix;18561857// Complain about misspelled directives.1858if (CheckTy == Check::CheckMisspelled) {1859StringRef UsedDirective(UsedPrefix.data(),1860AfterSuffix.data() - UsedPrefix.data());1861SM.PrintMessage(SMLoc::getFromPointer(UsedDirective.data()),1862SourceMgr::DK_Error,1863"misspelled directive '" + UsedDirective + "'");1864return true;1865}18661867// Complain about useful-looking but unsupported suffixes.1868if (CheckTy == Check::CheckBadNot) {1869SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,1870"unsupported -NOT combo on prefix '" + UsedPrefix + "'");1871return true;1872}18731874// Complain about invalid count specification.1875if (CheckTy == Check::CheckBadCount) {1876SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error,1877"invalid count in -COUNT specification on prefix '" +1878UsedPrefix + "'");1879return true;1880}18811882// Okay, we found the prefix, yay. Remember the rest of the line, but ignore1883// leading whitespace.1884if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))1885Buffer = Buffer.substr(Buffer.find_first_not_of(" \t"));18861887// Scan ahead to the end of line.1888size_t EOL = Buffer.find_first_of("\n\r");18891890// Remember the location of the start of the pattern, for diagnostics.1891SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());18921893// Extract the pattern from the buffer.1894StringRef PatternBuffer = Buffer.substr(0, EOL);1895Buffer = Buffer.substr(EOL);18961897// If this is a comment, we're done.1898if (CheckTy == Check::CheckComment)1899continue;19001901// Parse the pattern.1902Pattern P(CheckTy, PatternContext.get(), LineNumber);1903if (P.parsePattern(PatternBuffer, UsedPrefix, SM, Req))1904return true;19051906// Verify that CHECK-LABEL lines do not define or use variables1907if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {1908SM.PrintMessage(1909SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error,1910"found '" + UsedPrefix + "-LABEL:'"1911" with variable definition or use");1912return true;1913}19141915// Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them.1916if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame ||1917CheckTy == Check::CheckEmpty) &&1918CheckStrings->empty()) {1919StringRef Type = CheckTy == Check::CheckNext1920? "NEXT"1921: CheckTy == Check::CheckEmpty ? "EMPTY" : "SAME";1922SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),1923SourceMgr::DK_Error,1924"found '" + UsedPrefix + "-" + Type +1925"' without previous '" + UsedPrefix + ": line");1926return true;1927}19281929// Handle CHECK-DAG/-NOT.1930if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) {1931DagNotMatches.emplace_back(P, UsedPrefix);1932continue;1933}19341935// Okay, add the string we captured to the output vector and move on.1936CheckStrings->emplace_back(P, UsedPrefix, PatternLoc);1937std::swap(DagNotMatches, CheckStrings->back().DagNotStrings);1938DagNotMatches = ImplicitNegativeChecks;1939}19401941// When there are no used prefixes we report an error except in the case that1942// no prefix is specified explicitly but -implicit-check-not is specified.1943const bool NoPrefixesFound = PrefixesNotFound.size() == DistinctPrefixes;1944const bool SomePrefixesUnexpectedlyNotUsed =1945!Req.AllowUnusedPrefixes && !PrefixesNotFound.empty();1946if ((NoPrefixesFound || SomePrefixesUnexpectedlyNotUsed) &&1947(ImplicitNegativeChecks.empty() || !Req.IsDefaultCheckPrefix)) {1948errs() << "error: no check strings found with prefix"1949<< (PrefixesNotFound.size() > 1 ? "es " : " ");1950bool First = true;1951for (StringRef MissingPrefix : PrefixesNotFound) {1952if (!First)1953errs() << ", ";1954errs() << "\'" << MissingPrefix << ":'";1955First = false;1956}1957errs() << '\n';1958return true;1959}19601961// Add an EOF pattern for any trailing --implicit-check-not/CHECK-DAG/-NOTs,1962// and use the first prefix as a filler for the error message.1963if (!DagNotMatches.empty()) {1964CheckStrings->emplace_back(1965Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1),1966*Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));1967std::swap(DagNotMatches, CheckStrings->back().DagNotStrings);1968}19691970return false;1971}19721973/// Returns either (1) \c ErrorSuccess if there was no error or (2)1974/// \c ErrorReported if an error was reported, such as an unexpected match.1975static Error printMatch(bool ExpectedMatch, const SourceMgr &SM,1976StringRef Prefix, SMLoc Loc, const Pattern &Pat,1977int MatchedCount, StringRef Buffer,1978Pattern::MatchResult MatchResult,1979const FileCheckRequest &Req,1980std::vector<FileCheckDiag> *Diags) {1981// Suppress some verbosity if there's no error.1982bool HasError = !ExpectedMatch || MatchResult.TheError;1983bool PrintDiag = true;1984if (!HasError) {1985if (!Req.Verbose)1986return ErrorReported::reportedOrSuccess(HasError);1987if (!Req.VerboseVerbose && Pat.getCheckTy() == Check::CheckEOF)1988return ErrorReported::reportedOrSuccess(HasError);1989// Due to their verbosity, we don't print verbose diagnostics here if we're1990// gathering them for Diags to be rendered elsewhere, but we always print1991// other diagnostics.1992PrintDiag = !Diags;1993}19941995// Add "found" diagnostic, substitutions, and variable definitions to Diags.1996FileCheckDiag::MatchType MatchTy = ExpectedMatch1997? FileCheckDiag::MatchFoundAndExpected1998: FileCheckDiag::MatchFoundButExcluded;1999SMRange MatchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(),2000Buffer, MatchResult.TheMatch->Pos,2001MatchResult.TheMatch->Len, Diags);2002if (Diags) {2003Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, Diags);2004Pat.printVariableDefs(SM, MatchTy, Diags);2005}2006if (!PrintDiag) {2007assert(!HasError && "expected to report more diagnostics for error");2008return ErrorReported::reportedOrSuccess(HasError);2009}20102011// Print the match.2012std::string Message = formatv("{0}: {1} string found in input",2013Pat.getCheckTy().getDescription(Prefix),2014(ExpectedMatch ? "expected" : "excluded"))2015.str();2016if (Pat.getCount() > 1)2017Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();2018SM.PrintMessage(2019Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message);2020SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here",2021{MatchRange});20222023// Print additional information, which can be useful even if there are errors.2024Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, nullptr);2025Pat.printVariableDefs(SM, MatchTy, nullptr);20262027// Print errors and add them to Diags. We report these errors after the match2028// itself because we found them after the match. If we had found them before2029// the match, we'd be in printNoMatch.2030handleAllErrors(std::move(MatchResult.TheError),2031[&](const ErrorDiagnostic &E) {2032E.log(errs());2033if (Diags) {2034Diags->emplace_back(SM, Pat.getCheckTy(), Loc,2035FileCheckDiag::MatchFoundErrorNote,2036E.getRange(), E.getMessage().str());2037}2038});2039return ErrorReported::reportedOrSuccess(HasError);2040}20412042/// Returns either (1) \c ErrorSuccess if there was no error, or (2)2043/// \c ErrorReported if an error was reported, such as an expected match not2044/// found.2045static Error printNoMatch(bool ExpectedMatch, const SourceMgr &SM,2046StringRef Prefix, SMLoc Loc, const Pattern &Pat,2047int MatchedCount, StringRef Buffer, Error MatchError,2048bool VerboseVerbose,2049std::vector<FileCheckDiag> *Diags) {2050// Print any pattern errors, and record them to be added to Diags later.2051bool HasError = ExpectedMatch;2052bool HasPatternError = false;2053FileCheckDiag::MatchType MatchTy = ExpectedMatch2054? FileCheckDiag::MatchNoneButExpected2055: FileCheckDiag::MatchNoneAndExcluded;2056SmallVector<std::string, 4> ErrorMsgs;2057handleAllErrors(2058std::move(MatchError),2059[&](const ErrorDiagnostic &E) {2060HasError = HasPatternError = true;2061MatchTy = FileCheckDiag::MatchNoneForInvalidPattern;2062E.log(errs());2063if (Diags)2064ErrorMsgs.push_back(E.getMessage().str());2065},2066// NotFoundError is why printNoMatch was invoked.2067[](const NotFoundError &E) {});20682069// Suppress some verbosity if there's no error.2070bool PrintDiag = true;2071if (!HasError) {2072if (!VerboseVerbose)2073return ErrorReported::reportedOrSuccess(HasError);2074// Due to their verbosity, we don't print verbose diagnostics here if we're2075// gathering them for Diags to be rendered elsewhere, but we always print2076// other diagnostics.2077PrintDiag = !Diags;2078}20792080// Add "not found" diagnostic, substitutions, and pattern errors to Diags.2081//2082// We handle Diags a little differently than the errors we print directly:2083// we add the "not found" diagnostic to Diags even if there are pattern2084// errors. The reason is that we need to attach pattern errors as notes2085// somewhere in the input, and the input search range from the "not found"2086// diagnostic is all we have to anchor them.2087SMRange SearchRange = ProcessMatchResult(MatchTy, SM, Loc, Pat.getCheckTy(),2088Buffer, 0, Buffer.size(), Diags);2089if (Diags) {2090SMRange NoteRange = SMRange(SearchRange.Start, SearchRange.Start);2091for (StringRef ErrorMsg : ErrorMsgs)2092Diags->emplace_back(SM, Pat.getCheckTy(), Loc, MatchTy, NoteRange,2093ErrorMsg);2094Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, Diags);2095}2096if (!PrintDiag) {2097assert(!HasError && "expected to report more diagnostics for error");2098return ErrorReported::reportedOrSuccess(HasError);2099}21002101// Print "not found" diagnostic, except that's implied if we already printed a2102// pattern error.2103if (!HasPatternError) {2104std::string Message = formatv("{0}: {1} string not found in input",2105Pat.getCheckTy().getDescription(Prefix),2106(ExpectedMatch ? "expected" : "excluded"))2107.str();2108if (Pat.getCount() > 1)2109Message +=2110formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str();2111SM.PrintMessage(Loc,2112ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark,2113Message);2114SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note,2115"scanning from here");2116}21172118// Print additional information, which can be useful even after a pattern2119// error.2120Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, nullptr);2121if (ExpectedMatch)2122Pat.printFuzzyMatch(SM, Buffer, Diags);2123return ErrorReported::reportedOrSuccess(HasError);2124}21252126/// Returns either (1) \c ErrorSuccess if there was no error, or (2)2127/// \c ErrorReported if an error was reported.2128static Error reportMatchResult(bool ExpectedMatch, const SourceMgr &SM,2129StringRef Prefix, SMLoc Loc, const Pattern &Pat,2130int MatchedCount, StringRef Buffer,2131Pattern::MatchResult MatchResult,2132const FileCheckRequest &Req,2133std::vector<FileCheckDiag> *Diags) {2134if (MatchResult.TheMatch)2135return printMatch(ExpectedMatch, SM, Prefix, Loc, Pat, MatchedCount, Buffer,2136std::move(MatchResult), Req, Diags);2137return printNoMatch(ExpectedMatch, SM, Prefix, Loc, Pat, MatchedCount, Buffer,2138std::move(MatchResult.TheError), Req.VerboseVerbose,2139Diags);2140}21412142/// Counts the number of newlines in the specified range.2143static unsigned CountNumNewlinesBetween(StringRef Range,2144const char *&FirstNewLine) {2145unsigned NumNewLines = 0;2146while (true) {2147// Scan for newline.2148Range = Range.substr(Range.find_first_of("\n\r"));2149if (Range.empty())2150return NumNewLines;21512152++NumNewLines;21532154// Handle \n\r and \r\n as a single newline.2155if (Range.size() > 1 && (Range[1] == '\n' || Range[1] == '\r') &&2156(Range[0] != Range[1]))2157Range = Range.substr(1);2158Range = Range.substr(1);21592160if (NumNewLines == 1)2161FirstNewLine = Range.begin();2162}2163}21642165size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,2166bool IsLabelScanMode, size_t &MatchLen,2167FileCheckRequest &Req,2168std::vector<FileCheckDiag> *Diags) const {2169size_t LastPos = 0;2170std::vector<const DagNotPrefixInfo *> NotStrings;21712172// IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL2173// bounds; we have not processed variable definitions within the bounded block2174// yet so cannot handle any final CHECK-DAG yet; this is handled when going2175// over the block again (including the last CHECK-LABEL) in normal mode.2176if (!IsLabelScanMode) {2177// Match "dag strings" (with mixed "not strings" if any).2178LastPos = CheckDag(SM, Buffer, NotStrings, Req, Diags);2179if (LastPos == StringRef::npos)2180return StringRef::npos;2181}21822183// Match itself from the last position after matching CHECK-DAG.2184size_t LastMatchEnd = LastPos;2185size_t FirstMatchPos = 0;2186// Go match the pattern Count times. Majority of patterns only match with2187// count 1 though.2188assert(Pat.getCount() != 0 && "pattern count can not be zero");2189for (int i = 1; i <= Pat.getCount(); i++) {2190StringRef MatchBuffer = Buffer.substr(LastMatchEnd);2191// get a match at current start point2192Pattern::MatchResult MatchResult = Pat.match(MatchBuffer, SM);21932194// report2195if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, Prefix, Loc,2196Pat, i, MatchBuffer,2197std::move(MatchResult), Req, Diags)) {2198cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));2199return StringRef::npos;2200}22012202size_t MatchPos = MatchResult.TheMatch->Pos;2203if (i == 1)2204FirstMatchPos = LastPos + MatchPos;22052206// move start point after the match2207LastMatchEnd += MatchPos + MatchResult.TheMatch->Len;2208}2209// Full match len counts from first match pos.2210MatchLen = LastMatchEnd - FirstMatchPos;22112212// Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT2213// or CHECK-NOT2214if (!IsLabelScanMode) {2215size_t MatchPos = FirstMatchPos - LastPos;2216StringRef MatchBuffer = Buffer.substr(LastPos);2217StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos);22182219// If this check is a "CHECK-NEXT", verify that the previous match was on2220// the previous line (i.e. that there is one newline between them).2221if (CheckNext(SM, SkippedRegion)) {2222ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,2223Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,2224Diags, Req.Verbose);2225return StringRef::npos;2226}22272228// If this check is a "CHECK-SAME", verify that the previous match was on2229// the same line (i.e. that there is no newline between them).2230if (CheckSame(SM, SkippedRegion)) {2231ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc,2232Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen,2233Diags, Req.Verbose);2234return StringRef::npos;2235}22362237// If this match had "not strings", verify that they don't exist in the2238// skipped region.2239if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))2240return StringRef::npos;2241}22422243return FirstMatchPos;2244}22452246bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const {2247if (Pat.getCheckTy() != Check::CheckNext &&2248Pat.getCheckTy() != Check::CheckEmpty)2249return false;22502251Twine CheckName =2252Prefix +2253Twine(Pat.getCheckTy() == Check::CheckEmpty ? "-EMPTY" : "-NEXT");22542255// Count the number of newlines between the previous match and this one.2256const char *FirstNewLine = nullptr;2257unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);22582259if (NumNewLines == 0) {2260SM.PrintMessage(Loc, SourceMgr::DK_Error,2261CheckName + ": is on the same line as previous match");2262SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,2263"'next' match was here");2264SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,2265"previous match ended here");2266return true;2267}22682269if (NumNewLines != 1) {2270SM.PrintMessage(Loc, SourceMgr::DK_Error,2271CheckName +2272": is not on the line after the previous match");2273SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,2274"'next' match was here");2275SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,2276"previous match ended here");2277SM.PrintMessage(SMLoc::getFromPointer(FirstNewLine), SourceMgr::DK_Note,2278"non-matching line after previous match is here");2279return true;2280}22812282return false;2283}22842285bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const {2286if (Pat.getCheckTy() != Check::CheckSame)2287return false;22882289// Count the number of newlines between the previous match and this one.2290const char *FirstNewLine = nullptr;2291unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine);22922293if (NumNewLines != 0) {2294SM.PrintMessage(Loc, SourceMgr::DK_Error,2295Prefix +2296"-SAME: is not on the same line as the previous match");2297SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note,2298"'next' match was here");2299SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note,2300"previous match ended here");2301return true;2302}23032304return false;2305}23062307bool FileCheckString::CheckNot(2308const SourceMgr &SM, StringRef Buffer,2309const std::vector<const DagNotPrefixInfo *> &NotStrings,2310const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const {2311bool DirectiveFail = false;2312for (auto NotInfo : NotStrings) {2313assert((NotInfo->DagNotPat.getCheckTy() == Check::CheckNot) &&2314"Expect CHECK-NOT!");2315Pattern::MatchResult MatchResult = NotInfo->DagNotPat.match(Buffer, SM);2316if (Error Err = reportMatchResult(2317/*ExpectedMatch=*/false, SM, NotInfo->DagNotPrefix,2318NotInfo->DagNotPat.getLoc(), NotInfo->DagNotPat, 1, Buffer,2319std::move(MatchResult), Req, Diags)) {2320cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));2321DirectiveFail = true;2322continue;2323}2324}2325return DirectiveFail;2326}23272328size_t2329FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,2330std::vector<const DagNotPrefixInfo *> &NotStrings,2331const FileCheckRequest &Req,2332std::vector<FileCheckDiag> *Diags) const {2333if (DagNotStrings.empty())2334return 0;23352336// The start of the search range.2337size_t StartPos = 0;23382339struct MatchRange {2340size_t Pos;2341size_t End;2342};2343// A sorted list of ranges for non-overlapping CHECK-DAG matches. Match2344// ranges are erased from this list once they are no longer in the search2345// range.2346std::list<MatchRange> MatchRanges;23472348// We need PatItr and PatEnd later for detecting the end of a CHECK-DAG2349// group, so we don't use a range-based for loop here.2350for (auto PatItr = DagNotStrings.begin(), PatEnd = DagNotStrings.end();2351PatItr != PatEnd; ++PatItr) {2352const Pattern &Pat = PatItr->DagNotPat;2353const StringRef DNPrefix = PatItr->DagNotPrefix;2354assert((Pat.getCheckTy() == Check::CheckDAG ||2355Pat.getCheckTy() == Check::CheckNot) &&2356"Invalid CHECK-DAG or CHECK-NOT!");23572358if (Pat.getCheckTy() == Check::CheckNot) {2359NotStrings.push_back(&*PatItr);2360continue;2361}23622363assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!");23642365// CHECK-DAG always matches from the start.2366size_t MatchLen = 0, MatchPos = StartPos;23672368// Search for a match that doesn't overlap a previous match in this2369// CHECK-DAG group.2370for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {2371StringRef MatchBuffer = Buffer.substr(MatchPos);2372Pattern::MatchResult MatchResult = Pat.match(MatchBuffer, SM);2373// With a group of CHECK-DAGs, a single mismatching means the match on2374// that group of CHECK-DAGs fails immediately.2375if (MatchResult.TheError || Req.VerboseVerbose) {2376if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, DNPrefix,2377Pat.getLoc(), Pat, 1, MatchBuffer,2378std::move(MatchResult), Req, Diags)) {2379cantFail(2380handleErrors(std::move(Err), [&](const ErrorReported &E) {}));2381return StringRef::npos;2382}2383}2384MatchLen = MatchResult.TheMatch->Len;2385// Re-calc it as the offset relative to the start of the original2386// string.2387MatchPos += MatchResult.TheMatch->Pos;2388MatchRange M{MatchPos, MatchPos + MatchLen};2389if (Req.AllowDeprecatedDagOverlap) {2390// We don't need to track all matches in this mode, so we just maintain2391// one match range that encompasses the current CHECK-DAG group's2392// matches.2393if (MatchRanges.empty())2394MatchRanges.insert(MatchRanges.end(), M);2395else {2396auto Block = MatchRanges.begin();2397Block->Pos = std::min(Block->Pos, M.Pos);2398Block->End = std::max(Block->End, M.End);2399}2400break;2401}2402// Iterate previous matches until overlapping match or insertion point.2403bool Overlap = false;2404for (; MI != ME; ++MI) {2405if (M.Pos < MI->End) {2406// !Overlap => New match has no overlap and is before this old match.2407// Overlap => New match overlaps this old match.2408Overlap = MI->Pos < M.End;2409break;2410}2411}2412if (!Overlap) {2413// Insert non-overlapping match into list.2414MatchRanges.insert(MI, M);2415break;2416}2417if (Req.VerboseVerbose) {2418// Due to their verbosity, we don't print verbose diagnostics here if2419// we're gathering them for a different rendering, but we always print2420// other diagnostics.2421if (!Diags) {2422SMLoc OldStart = SMLoc::getFromPointer(Buffer.data() + MI->Pos);2423SMLoc OldEnd = SMLoc::getFromPointer(Buffer.data() + MI->End);2424SMRange OldRange(OldStart, OldEnd);2425SM.PrintMessage(OldStart, SourceMgr::DK_Note,2426"match discarded, overlaps earlier DAG match here",2427{OldRange});2428} else {2429SMLoc CheckLoc = Diags->rbegin()->CheckLoc;2430for (auto I = Diags->rbegin(), E = Diags->rend();2431I != E && I->CheckLoc == CheckLoc; ++I)2432I->MatchTy = FileCheckDiag::MatchFoundButDiscarded;2433}2434}2435MatchPos = MI->End;2436}2437if (!Req.VerboseVerbose)2438cantFail(printMatch(2439/*ExpectedMatch=*/true, SM, DNPrefix, Pat.getLoc(), Pat, 1, Buffer,2440Pattern::MatchResult(MatchPos, MatchLen, Error::success()), Req,2441Diags));24422443// Handle the end of a CHECK-DAG group.2444if (std::next(PatItr) == PatEnd ||2445std::next(PatItr)->DagNotPat.getCheckTy() == Check::CheckNot) {2446if (!NotStrings.empty()) {2447// If there are CHECK-NOTs between two CHECK-DAGs or from CHECK to2448// CHECK-DAG, verify that there are no 'not' strings occurred in that2449// region.2450StringRef SkippedRegion =2451Buffer.slice(StartPos, MatchRanges.begin()->Pos);2452if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags))2453return StringRef::npos;2454// Clear "not strings".2455NotStrings.clear();2456}2457// All subsequent CHECK-DAGs and CHECK-NOTs should be matched from the2458// end of this CHECK-DAG group's match range.2459StartPos = MatchRanges.rbegin()->End;2460// Don't waste time checking for (impossible) overlaps before that.2461MatchRanges.clear();2462}2463}24642465return StartPos;2466}24672468static bool ValidatePrefixes(StringRef Kind, StringSet<> &UniquePrefixes,2469ArrayRef<StringRef> SuppliedPrefixes) {2470for (StringRef Prefix : SuppliedPrefixes) {2471if (Prefix.empty()) {2472errs() << "error: supplied " << Kind << " prefix must not be the empty "2473<< "string\n";2474return false;2475}2476static const Regex Validator("^[a-zA-Z0-9_-]*$");2477if (!Validator.match(Prefix)) {2478errs() << "error: supplied " << Kind << " prefix must start with a "2479<< "letter and contain only alphanumeric characters, hyphens, and "2480<< "underscores: '" << Prefix << "'\n";2481return false;2482}2483if (!UniquePrefixes.insert(Prefix).second) {2484errs() << "error: supplied " << Kind << " prefix must be unique among "2485<< "check and comment prefixes: '" << Prefix << "'\n";2486return false;2487}2488}2489return true;2490}24912492bool FileCheck::ValidateCheckPrefixes() {2493StringSet<> UniquePrefixes;2494// Add default prefixes to catch user-supplied duplicates of them below.2495if (Req.CheckPrefixes.empty()) {2496for (const char *Prefix : DefaultCheckPrefixes)2497UniquePrefixes.insert(Prefix);2498}2499if (Req.CommentPrefixes.empty()) {2500for (const char *Prefix : DefaultCommentPrefixes)2501UniquePrefixes.insert(Prefix);2502}2503// Do not validate the default prefixes, or diagnostics about duplicates might2504// incorrectly indicate that they were supplied by the user.2505if (!ValidatePrefixes("check", UniquePrefixes, Req.CheckPrefixes))2506return false;2507if (!ValidatePrefixes("comment", UniquePrefixes, Req.CommentPrefixes))2508return false;2509return true;2510}25112512Error FileCheckPatternContext::defineCmdlineVariables(2513ArrayRef<StringRef> CmdlineDefines, SourceMgr &SM) {2514assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&2515"Overriding defined variable with command-line variable definitions");25162517if (CmdlineDefines.empty())2518return Error::success();25192520// Create a string representing the vector of command-line definitions. Each2521// definition is on its own line and prefixed with a definition number to2522// clarify which definition a given diagnostic corresponds to.2523unsigned I = 0;2524Error Errs = Error::success();2525std::string CmdlineDefsDiag;2526SmallVector<std::pair<size_t, size_t>, 4> CmdlineDefsIndices;2527for (StringRef CmdlineDef : CmdlineDefines) {2528std::string DefPrefix = ("Global define #" + Twine(++I) + ": ").str();2529size_t EqIdx = CmdlineDef.find('=');2530if (EqIdx == StringRef::npos) {2531CmdlineDefsIndices.push_back(std::make_pair(CmdlineDefsDiag.size(), 0));2532continue;2533}2534// Numeric variable definition.2535if (CmdlineDef[0] == '#') {2536// Append a copy of the command-line definition adapted to use the same2537// format as in the input file to be able to reuse2538// parseNumericSubstitutionBlock.2539CmdlineDefsDiag += (DefPrefix + CmdlineDef + " (parsed as: [[").str();2540std::string SubstitutionStr = std::string(CmdlineDef);2541SubstitutionStr[EqIdx] = ':';2542CmdlineDefsIndices.push_back(2543std::make_pair(CmdlineDefsDiag.size(), SubstitutionStr.size()));2544CmdlineDefsDiag += (SubstitutionStr + Twine("]])\n")).str();2545} else {2546CmdlineDefsDiag += DefPrefix;2547CmdlineDefsIndices.push_back(2548std::make_pair(CmdlineDefsDiag.size(), CmdlineDef.size()));2549CmdlineDefsDiag += (CmdlineDef + "\n").str();2550}2551}25522553// Create a buffer with fake command line content in order to display2554// parsing diagnostic with location information and point to the2555// global definition with invalid syntax.2556std::unique_ptr<MemoryBuffer> CmdLineDefsDiagBuffer =2557MemoryBuffer::getMemBufferCopy(CmdlineDefsDiag, "Global defines");2558StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();2559SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc());25602561for (std::pair<size_t, size_t> CmdlineDefIndices : CmdlineDefsIndices) {2562StringRef CmdlineDef = CmdlineDefsDiagRef.substr(CmdlineDefIndices.first,2563CmdlineDefIndices.second);2564if (CmdlineDef.empty()) {2565Errs = joinErrors(2566std::move(Errs),2567ErrorDiagnostic::get(SM, CmdlineDef,2568"missing equal sign in global definition"));2569continue;2570}25712572// Numeric variable definition.2573if (CmdlineDef[0] == '#') {2574// Now parse the definition both to check that the syntax is correct and2575// to create the necessary class instance.2576StringRef CmdlineDefExpr = CmdlineDef.substr(1);2577std::optional<NumericVariable *> DefinedNumericVariable;2578Expected<std::unique_ptr<Expression>> ExpressionResult =2579Pattern::parseNumericSubstitutionBlock(CmdlineDefExpr,2580DefinedNumericVariable, false,2581std::nullopt, this, SM);2582if (!ExpressionResult) {2583Errs = joinErrors(std::move(Errs), ExpressionResult.takeError());2584continue;2585}2586std::unique_ptr<Expression> Expression = std::move(*ExpressionResult);2587// Now evaluate the expression whose value this variable should be set2588// to, since the expression of a command-line variable definition should2589// only use variables defined earlier on the command-line. If not, this2590// is an error and we report it.2591Expected<APInt> Value = Expression->getAST()->eval();2592if (!Value) {2593Errs = joinErrors(std::move(Errs), Value.takeError());2594continue;2595}25962597assert(DefinedNumericVariable && "No variable defined");2598(*DefinedNumericVariable)->setValue(*Value);25992600// Record this variable definition.2601GlobalNumericVariableTable[(*DefinedNumericVariable)->getName()] =2602*DefinedNumericVariable;2603} else {2604// String variable definition.2605std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');2606StringRef CmdlineName = CmdlineNameVal.first;2607StringRef OrigCmdlineName = CmdlineName;2608Expected<Pattern::VariableProperties> ParseVarResult =2609Pattern::parseVariable(CmdlineName, SM);2610if (!ParseVarResult) {2611Errs = joinErrors(std::move(Errs), ParseVarResult.takeError());2612continue;2613}2614// Check that CmdlineName does not denote a pseudo variable is only2615// composed of the parsed numeric variable. This catches cases like2616// "FOO+2" in a "FOO+2=10" definition.2617if (ParseVarResult->IsPseudo || !CmdlineName.empty()) {2618Errs = joinErrors(std::move(Errs),2619ErrorDiagnostic::get(2620SM, OrigCmdlineName,2621"invalid name in string variable definition '" +2622OrigCmdlineName + "'"));2623continue;2624}2625StringRef Name = ParseVarResult->Name;26262627// Detect collisions between string and numeric variables when the former2628// is created later than the latter.2629if (GlobalNumericVariableTable.contains(Name)) {2630Errs = joinErrors(std::move(Errs),2631ErrorDiagnostic::get(SM, Name,2632"numeric variable with name '" +2633Name + "' already exists"));2634continue;2635}2636GlobalVariableTable.insert(CmdlineNameVal);2637// Mark the string variable as defined to detect collisions between2638// string and numeric variables in defineCmdlineVariables when the latter2639// is created later than the former. We cannot reuse GlobalVariableTable2640// for this by populating it with an empty string since we would then2641// lose the ability to detect the use of an undefined variable in2642// match().2643DefinedVariableTable[Name] = true;2644}2645}26462647return Errs;2648}26492650void FileCheckPatternContext::clearLocalVars() {2651SmallVector<StringRef, 16> LocalPatternVars, LocalNumericVars;2652for (const StringMapEntry<StringRef> &Var : GlobalVariableTable)2653if (Var.first()[0] != '$')2654LocalPatternVars.push_back(Var.first());26552656// Numeric substitution reads the value of a variable directly, not via2657// GlobalNumericVariableTable. Therefore, we clear local variables by2658// clearing their value which will lead to a numeric substitution failure. We2659// also mark the variable for removal from GlobalNumericVariableTable since2660// this is what defineCmdlineVariables checks to decide that no global2661// variable has been defined.2662for (const auto &Var : GlobalNumericVariableTable)2663if (Var.first()[0] != '$') {2664Var.getValue()->clearValue();2665LocalNumericVars.push_back(Var.first());2666}26672668for (const auto &Var : LocalPatternVars)2669GlobalVariableTable.erase(Var);2670for (const auto &Var : LocalNumericVars)2671GlobalNumericVariableTable.erase(Var);2672}26732674bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer,2675std::vector<FileCheckDiag> *Diags) {2676bool ChecksFailed = false;26772678unsigned i = 0, j = 0, e = CheckStrings->size();2679while (true) {2680StringRef CheckRegion;2681if (j == e) {2682CheckRegion = Buffer;2683} else {2684const FileCheckString &CheckLabelStr = (*CheckStrings)[j];2685if (CheckLabelStr.Pat.getCheckTy() != Check::CheckLabel) {2686++j;2687continue;2688}26892690// Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG2691size_t MatchLabelLen = 0;2692size_t MatchLabelPos =2693CheckLabelStr.Check(SM, Buffer, true, MatchLabelLen, Req, Diags);2694if (MatchLabelPos == StringRef::npos)2695// Immediately bail if CHECK-LABEL fails, nothing else we can do.2696return false;26972698CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen);2699Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen);2700++j;2701}27022703// Do not clear the first region as it's the one before the first2704// CHECK-LABEL and it would clear variables defined on the command-line2705// before they get used.2706if (i != 0 && Req.EnableVarScope)2707PatternContext->clearLocalVars();27082709for (; i != j; ++i) {2710const FileCheckString &CheckStr = (*CheckStrings)[i];27112712// Check each string within the scanned region, including a second check2713// of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG)2714size_t MatchLen = 0;2715size_t MatchPos =2716CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags);27172718if (MatchPos == StringRef::npos) {2719ChecksFailed = true;2720i = j;2721break;2722}27232724CheckRegion = CheckRegion.substr(MatchPos + MatchLen);2725}27262727if (j == e)2728break;2729}27302731// Success if no checks failed.2732return !ChecksFailed;2733}273427352736