diff options
Diffstat (limited to 'lib/Support/FileCheck.cpp')
-rw-r--r-- | lib/Support/FileCheck.cpp | 1535 |
1 files changed, 1326 insertions, 209 deletions
diff --git a/lib/Support/FileCheck.cpp b/lib/Support/FileCheck.cpp index 37986c96c08..f0e2f32e55f 100644 --- a/lib/Support/FileCheck.cpp +++ b/lib/Support/FileCheck.cpp @@ -25,6 +25,829 @@ using namespace llvm; +/// Define format equality: formats are equal if all bits are identical. +bool FileCheckNumExprFmtType:: +operator==(const struct FileCheckNumExprFmtType &other) { + return Set == other.Set && Conflict == other.Conflict && + Signed == other.Signed && Hex == other.Hex && Cap == other.Cap; +} + +/// Equality operator: 2 values are equal if both are valid and have same +/// signedness and corresponding value. +bool FileCheckNumExprVal::operator==(const FileCheckNumExprVal &other) { + if (!Valid || !other.Valid) + return false; + + if (Signed) + return SVal == other.SVal; + else + return UVal == other.UVal; +} + +/// Convert value to a signed value, or mark value invalid if not possible +/// (original value was not within range for a signed integer). +void FileCheckNumExprVal::ConvertSigned() { + if (!Valid || Signed) + return; + + if (UVal > std::numeric_limits<int64_t>::max()) { + Valid = false; + return; + } + + SVal = UVal; + Signed = true; +} + +/// Convert value to an unsigned value, or mark value invalid if not possible +/// (original value was not within range for an unsigned integer). +void FileCheckNumExprVal::ConvertUnsigned() { + if (!Valid || !Signed) + return; + + if (SVal < 0) { + Valid = false; + return; + } + + UVal = SVal; + Signed = false; + return; +} + +/// Store in \p StringRepr a string representation of this value given the +/// matching format \p Fmt. Return whether value had no such a string +/// representation (true if value is invalid). +bool FileCheckNumExprVal::GetStringRepr(struct FileCheckNumExprFmtType Fmt, + std::string &StringRepr) const { + if (!Valid) + return true; + + if (Fmt.Hex) { + StringRepr = utohexstr(GetUnsignedValue(), !Fmt.Cap); + } else if (Fmt.Signed) + StringRepr = itostr(GetSignedValue()); + else + StringRepr = utostr(GetUnsignedValue()); + + return false; +} + +/// Perform an addition operation. Return an invalid value in case of +/// underflow or overflow. +FileCheckNumExprVal FileCheckNumExprVal::Add(const FileCheckNumExprVal &Op1, + const FileCheckNumExprVal &Op2) { + // Operands must be valid. + if (!Op1.Valid || !Op2.Valid) + return FileCheckNumExprVal(); + + // Operands must have same sign. + if (Op1.Signed != Op2.Signed) + return FileCheckNumExprVal(); + + if (Op1.Signed) { + int64_t Val1 = Op1.SVal; + int64_t Val2 = Op2.SVal; + + // Op1 + Op2 > max int64_t. + if (Val1 > 0 && Val2 > 0 && + Val1 > (std::numeric_limits<int64_t>::max() - Val2)) + return FileCheckNumExprVal(); + + // Op1 + Op2 < min int64_t. + if (Val1 < 0 && Val2 < 0 && + Val1 < (std::numeric_limits<int64_t>::min() - Val2)) + return FileCheckNumExprVal(); + + return FileCheckNumExprVal(Val1 + Val2); + } else { + uint64_t Val1 = Op1.UVal; + uint64_t Val2 = Op2.UVal; + + // Op1 + Op2 > max uint64_t. + if (Val1 > std::numeric_limits<uint64_t>::max() - Val2) + return FileCheckNumExprVal(); + return FileCheckNumExprVal(Val1 + Val2); + } +} + +/// Perform a substraction operation. Return an invalid value in case of +/// underflow or overflow. +FileCheckNumExprVal FileCheckNumExprVal::Sub(const FileCheckNumExprVal &Op1, + const FileCheckNumExprVal &Op2) { + // Operands must be valid. + if (!Op1.Valid || !Op2.Valid) + return FileCheckNumExprVal(); + + // Operands must have same sign. + if (Op1.Signed != Op2.Signed) + return FileCheckNumExprVal(); + + if (Op1.Signed) { + int64_t Val1 = Op1.SVal; + int64_t Val2 = Op2.SVal; + + // Op1 - Op2 > max int64_t. + if (Val1 > 0 && Val2 < 0 && + Val1 > (std::numeric_limits<int64_t>::max() + Val2)) + return FileCheckNumExprVal(); + + // Op1 - Op2 < min int64_t. + if (Val1 < 0 && Val2 > 0 && + Val1 < (std::numeric_limits<int64_t>::min() + Val2)) + return FileCheckNumExprVal(); + + return FileCheckNumExprVal(Val1 - Val2); + } else { + uint64_t Val1 = Op1.UVal; + uint64_t Val2 = Op2.UVal; + + // Op1 < Op2. + if (Val1 < Val2) + return FileCheckNumExprVal(); + return FileCheckNumExprVal(Val1 - Val2); + } +} + +/// Evaluate the value of this literal. Therefore returns this node itself and +/// set \p Fmt to null since literals do not carry any implicit conversion. +FileCheckNumExprVal +FileCheckNumExprLiteral::Eval(struct FileCheckNumExprFmtType &Fmt) { + Fmt = FmtNone; + return Value; +} + +/// Generic constructor for a numeric expression whose equality constraint is +/// represented by \p AST, matching format is \p Fmt and whose AST can be +/// evaluated in phase \p KnownPhase. If matching format is unset (ie. no +/// explicit or implicit matching format), set it to default one (unsigned +/// decimal integer). +FileCheckNumExpr::FileCheckNumExpr(std::shared_ptr<FileCheckNumExprAST> AST, + struct FileCheckNumExprFmtType Fmt, + enum Check::FileCheckPhase KnownPhase) + : AST(AST), KnownPhase(KnownPhase) { + if (!Fmt.Set) + this->Fmt = FmtUnsigned; + else + this->Fmt = Fmt; +} + +/// Constructor for numeric expression with a known value in parse phase, +/// eg. the numeric expression defining the @LINE numeric variable (and +/// currently only used for that). +FileCheckNumExpr::FileCheckNumExpr(FileCheckNumExprVal Value, + struct FileCheckNumExprFmtType Fmt) + : FileCheckNumExpr(nullptr, Fmt, Check::ParsePhase) { + this->Value = Value; + this->KnownPhase = Check::ParsePhase; +} + +/// Set Value to \p Val if currently invalid or tentative. Return whether +/// Value failed to be set (Value was already valid and final). +bool FileCheckNumExpr::SetTentativeValue(FileCheckNumExprVal Val) { + // Allow a value to be tentatively set several time. + if (Value.IsValid() && !Value.IsTentative()) + return true; + + // Value should be set from evaluating the AST if any. + if (AST != nullptr) + return true; + + assert(!Val.IsTentative() && "Setting from already tentative matched value"); + Val.ToggleTentative(); + Value = Val; + return false; +} + +/// Store in \p Val the value corresponding to string representation \p StrVal +/// according to matching format of this numeric expression. Return whether +/// \p StrVal correspond to a valid and representable value. +bool FileCheckNumExpr::ValueFromStringRepr(StringRef StrVal, + FileCheckNumExprVal &Val) const { + unsigned Radix = Fmt.Hex ? 16 : 10; + if (Fmt.Signed) { + int64_t SVal; + + if (StrVal.getAsInteger(Radix, SVal)) + return true; + + Val = FileCheckNumExprVal(SVal); + } else { + uint64_t UVal; + + if (StrVal.getAsInteger(Radix, UVal)) + return true; + + Val = FileCheckNumExprVal(UVal); + } + + return false; +} + +/// Store in \p MatchString the regexp pattern to use to match this numeric +/// expression given its matching format. Return whether the function was +/// unable to determine the regexp pattern. +bool FileCheckNumExpr::GetMatchString(enum Check::FileCheckPhase Phase, + std::string &MatchString) { + assert(Phase <= Check::MatchPhase && "Asking match string after match done"); + + // We know the value, return its string representation. If supporting other + // constraints than equality this should be restricted to equality only. + if (Phase >= KnownPhase) { + if (!Value.IsValid()) + return true; + + if (Value.GetStringRepr(Fmt, MatchString)) + return true; + return false; + // We do not know the value but we will at match time. + } else if (KnownPhase <= Check::MatchPhase) { + MatchString = std::string(); + return false; + // We do not know the value and will not know it at match time. Output + // appropriate wildcard pattern. + } else { + assert(Fmt.Set && !(Fmt.Hex && Fmt.Signed) && + "Numeric expression with unexpected conversion"); + if (Fmt.Hex) { + if (Fmt.Cap) + MatchString = std::string("[[:digit:]A-F]+"); + else + MatchString = std::string("[[:digit:]a-f]+"); + } else if (Fmt.Signed) + MatchString = std::string("-?[[:digit:]]+"); + else + MatchString = std::string("[[:digit:]]+"); + + return false; + } +} + +/// Verify that the matched value in \p MatchedValue satisfies the constraint +/// expressed by this expression. Return true if constraint is not satisfied. +bool FileCheckNumExpr::VerifyConstraint( + FileCheckNumExprVal MatchedValue) const { + // Nothing to verify: numeric variable definition with empty numeric + // expression. + if (AST == nullptr) + return false; + + // Will fail if Value or MatchedValue are invalid. + return MatchedValue != Value; +} + +/// Store in \p EvaluatedValue the value evaluated from the constraint of this +/// numeric expression. Return whether evaluation failed. +bool FileCheckNumExpr::Eval(FileCheckNumExprVal &EvaluatedValue) const { + // Nothing to evaluate: numeric variable definition with empty numeric + // expression. + if (AST == nullptr) + return true; + + FileCheckNumExprFmtType ImplicitFmt; + EvaluatedValue = AST->Eval(ImplicitFmt); + if (!EvaluatedValue.IsValid()) + return true; + + // If expression comes with an explicit printing format different from the + // implicit one, signedness of EvaluatedValue could be different from the + // matched value. Convert to signedness of expression to ensure value + // compare equally. + if (Fmt.Signed) + EvaluatedValue.ConvertSigned(); + else + EvaluatedValue.ConvertUnsigned(); + if (!EvaluatedValue.IsValid()) + return true; + + return false; +} + +/// Set Value from the result of calling Eval(). Return whether it failed. +bool FileCheckNumExpr::SetValueFromEval() { + // No need to evaluate, we already have a final value. + if (Value.IsValid() && !Value.IsTentative()) + return true; + + FileCheckNumExprVal EvaluatedValue; + if (Eval(EvaluatedValue)) + return true; + + Value = EvaluatedValue; + return false; +} + +/// Mark value as final. Return whether value was already final. +bool FileCheckNumExpr::CommitValue() { + // Value is not a valid tentative value. + if (!Value.IsValid() || !Value.IsTentative()) + return true; + + Value.ToggleTentative(); + return false; +} + +/// Constructor for numeric variable \p Name with a known \p Value at parse +/// time (eg. the @LINE numeric variable). Matching format of the variable is +/// given in \p Fmt and \p DefLineNumber indicates when does this variable +/// starts to be defined. +FileCheckNumExprVar::FileCheckNumExprVar(StringRef Name, + FileCheckNumExprVal Value, + struct FileCheckNumExprFmtType Fmt, + unsigned DefLineNumber) + : Name(Name), DefLineNumber(DefLineNumber) { + this->NumExpr = std::make_shared<FileCheckNumExpr>(Value, Fmt); +} + +/// Evaluate the value of this numeric variable. Therefore returns the value +/// and implicit conversion of this numeric variable stored in its +/// corresponding numeric expression class. +FileCheckNumExprVal +FileCheckNumExprVar::Eval(struct FileCheckNumExprFmtType &Fmt) { + // Undefined variable. + if (NumExpr == nullptr) + return FileCheckNumExprVal(); + Fmt = NumExpr->GetFormat(); + return NumExpr->GetValue(); +} + +/// Append numeric variable's name to UndefVarNames if undefined. +void FileCheckNumExprVar::GetUndefVarNames( + std::vector<StringRef> &UndefVarNames) const { + if (NumExpr == nullptr) + UndefVarNames.emplace_back(Name); +} + +/// Evaluate the value of the binary operation represented by this AST. Uses +/// EvalBinop to perform the binary operation on the values of recursively +/// evaluating the left and right operands. +FileCheckNumExprVal +FileCheckASTBinop::Eval(struct FileCheckNumExprFmtType &Fmt) { + struct FileCheckNumExprFmtType LFmt, RFmt; + FileCheckNumExprVal LVal = Opl->Eval(LFmt); + FileCheckNumExprVal RVal = Opr->Eval(RFmt); + + // Integer promotion. + if (!LVal.IsSigned() || !RVal.IsSigned()) { + LVal.ConvertUnsigned(); + RVal.ConvertUnsigned(); + } + + if (!LVal.IsValid() || !RVal.IsValid()) + return FileCheckNumExprVal(); + + Fmt = (LFmt.Set) ? LFmt : RFmt; + if (LFmt.Set && RFmt.Set && LFmt != RFmt) + Fmt.Conflict = 1; + + FileCheckNumExprVal Val = EvalBinop(LVal, RVal); + + // Mark result as tentative if any of the operand was tentative. Assert + // result was not tentative beforehand to have that check only here. + assert(!Val.IsTentative() && "Binary operation returned tentative value"); + if (LVal.IsTentative() || RVal.IsTentative()) + Val.ToggleTentative(); + return Val; +} + +/// Append to UnderVarNames the names of undefined numeric variables used in +/// any of the operands. +void FileCheckASTBinop::GetUndefVarNames( + std::vector<StringRef> &UndefVarNames) const { + Opl->GetUndefVarNames(UndefVarNames); + Opr->GetUndefVarNames(UndefVarNames); +} + +/// Perform in \p SubstValue the substitution represented by this class +/// instance. For numeric variable we replace it by its value if known at +/// match time or a suitable wildcard pattern otherwise. For pattern variable +/// we simply replace it by the text its definition matched. Return whether +/// substitution failed. +bool FileCheckPatternSubst::Substitute(std::string &SubstValue) const { + if (isNumExpr) { + if (NumExpr->GetKnownPhase() == Check::MatchPhase) { + if (NumExpr->SetValueFromEval()) + return true; + } + if (NumExpr->GetMatchString(Check::MatchPhase, SubstValue)) + return true; + return false; + } else { + // Look up the value and escape it so that we can put it into the + // regex. + StringRef VarVal; + if (Context->GetPatternVarValue(SubstStr, VarVal)) + return true; + SubstValue = Regex::escape(VarVal); + } + + return false; +} + +/// Write in \p MatchedValue the value successfully matched (ie. constraint is +/// verified for a numeric expression) by the substitution. Return whether +/// match failed for that substitution. +bool FileCheckPatternSubst::MatchedValue(std::string &MatchedValue) const { + if (isNumExpr) { + // This will return true if NumExpr's value is invalid, which it will be if + // NumExpr did not match or the matched value did not satisfy the + // expression constraint. + return NumExpr->GetValueStringRepr(MatchedValue); + } else { + // Look up the value and escape it so that we can put it into the + // regex. + StringRef VarVal; + if (Context->GetPatternVarValue(SubstStr, VarVal)) + return true; + MatchedValue = Regex::escape(VarVal); + } + + return false; +} + +/// Append to UnderVarNames the names of undefined pattern or numeric variables +/// used in this substitution. +void FileCheckPatternSubst::GetUndefVarNames( + std::vector<StringRef> &UndefVarNames) const { + if (isNumExpr) + NumExpr->GetAST()->GetUndefVarNames(UndefVarNames); + else { + StringRef VarVal; + if (Context->GetPatternVarValue(SubstStr, VarVal)) + UndefVarNames.emplace_back(SubstStr); + } +} + +/// Parsing helper function that strips all leading whitespace from \p s. +static inline void SkipWhitespace(StringRef &s) { s = s.ltrim(" \t"); } + +/// Parsing helper function that strips the string in \p SkipStr from \p s. +/// Returns true if string \p SkipStr was not in \p s and \p Optional was +/// false. Returns false otherwise. +static bool Skip(StringRef &s, const StringRef &SkipStr, bool Optional) { + if (!s.consume_front(SkipStr) && !Optional) + return true; + return false; +} + +/// Parsing helper function that strips the first character in \p s and returns +/// it. +static char Next(StringRef &s) { + char c = s.front(); + s = s.drop_front(); + return c; +} + +// Verify that the string in \p Str or at the start of \p Str (if \p +// AllowTrailer is true) is a well formed variable name. Also set IsPseudo to +// true if it is a pseudo variable. +bool FileCheckPattern::ParseVariable(StringRef Str, bool &IsPseudo, + unsigned &TrailIdx) const { + bool ParsedOneChar = false; + unsigned i = 0; + IsPseudo = false; + for (unsigned e = Str.size(); i != e; ++i) { + if (i == 0) { + // Global vars start with '$'. + if (Str[i] == '$') + continue; + else if (Str[i] == '@') { + IsPseudo = true; + continue; + } + } + // Variable names are composed of alphanumeric characters and underscores. + if (Str[i] != '_' && !isalnum(Str[i])) + break; + ParsedOneChar = true; + } + + // Empty name. + if (!ParsedOneChar) + return true; + + TrailIdx = i; + StringRef Name = Str.substr(0, i); + + // Name can't start with a digit. + if (isdigit(static_cast<unsigned char>(Name[0]))) + return true; + + return false; +} + +/// Parse \p Expr for use or definition of a numeric variable. Return the +/// class instance representing the corresponding variable definition to be +/// used in the numeric expression AST in the case of a use, or nullptr if +/// parsing fails in which case errors are reported on \p SM. If +/// \p IsDefinition is true, a new instance is created. Likewise in case of +/// use if the variable is undefined at that point in the pattern. +std::shared_ptr<FileCheckNumExprVar> +FileCheckPattern::ParseNumericVariable(StringRef &Expr, bool IsDefinition, + const SourceMgr &SM) const { + bool IsPseudo; + unsigned TrailIdx; + + if (ParseVariable(Expr, IsPseudo, TrailIdx)) + return nullptr; + StringRef Name = Expr.substr(0, TrailIdx); + Expr = Expr.substr(TrailIdx); + + if (IsPseudo && !Name.equals("@LINE")) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "Unsupported pseudo variable '" + Name + "'"); + return nullptr; + } + if (IsDefinition && IsPseudo) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "Definition of pseudo variable '" + Name + "' unsupported"); + return nullptr; + } + + if (IsDefinition) { + // Detect collision between pattern and numeric variable when the latter + // is created on a later line than the former. + if (Context->DefinedVariableTable.find(Name) != + Context->DefinedVariableTable.end()) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "Pattern variable with same name '" + Name + "' exists"); + return nullptr; + } + return std::make_shared<FileCheckNumExprVar>(Name, this->LineNumber); + } else { + // This method is indirectly called from ParsePattern for all numeric + // variable definition and uses in the order in which they appear in the + // CHECK pattern. For each definition, the pointer to the corresponding + // AST class instance is stored in GlobalNumericVariableTable. Therefore + // the pointer we get below is for the AST class instance corresponding to + // the last definition of the variable before this use. + auto git = Context->GlobalNumericVariableTable.find(Name); + if (git != Context->GlobalNumericVariableTable.end()) + return git->second; + + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "Using undefined numeric variable '" + Name + "'"); + return std::make_shared<FileCheckNumExprVar>(Name, 0); + } +} + +/// Parse \p Expr for use of a numeric operand. Accept both literal values +/// and numeric variables. Return the class representing that operand in the +/// AST of the numeric expression or nullptr if parsing fails in which case +/// errors are reported on \p SM. Also return in \p ImplicitFmt the implicit +/// matching format of the operand and in \p KnownPhase the FileCheck phase +/// when the value of that operand is known. +std::shared_ptr<FileCheckNumExprAST> FileCheckPattern::ParseNumericOperand( + StringRef &Expr, const SourceMgr &SM, + struct FileCheckNumExprFmtType &ImplicitFmt, + enum Check::FileCheckPhase &KnownPhase) const { + + // Try to parse as a numeric variable use. + std::shared_ptr<FileCheckNumExprVar> NumVar = + ParseNumericVariable(Expr, false, SM); + if (NumVar != nullptr) { + FileCheckNumExpr *NumExpr = NumVar->GetNumExpr(); + // Variable is not undefined. + if (NumExpr != nullptr) { + ImplicitFmt = NumExpr->GetFormat(); + if (NumExpr->GetKnownPhase() == Check::ParsePhase) + KnownPhase = Check::ParsePhase; + // Variable defined on earlier line. Its value will thus be known when + // matching this use. + else if (NumVar->GetDefLineNumber() < LineNumber) + KnownPhase = Check::MatchPhase; + else + KnownPhase = Check::CheckPhase; + } + return NumVar; + } + + // Otherwise, parse it as a literal. + int64_t SignedLiteralValue; + uint64_t UnsignedLiteralValue; + StringRef SaveExpr = Expr; + // Accept both signed and unsigned literal. + if (!Expr.consumeInteger(0, SignedLiteralValue)) { + ImplicitFmt = FmtNone; + KnownPhase = Check::ParsePhase; + return std::make_shared<FileCheckNumExprLiteral>(SignedLiteralValue); + } else { + Expr = SaveExpr; + if (!Expr.consumeInteger(0, UnsignedLiteralValue)) { + ImplicitFmt = FmtNone; + KnownPhase = Check::ParsePhase; + return std::make_shared<FileCheckNumExprLiteral>(UnsignedLiteralValue); + } else + return nullptr; + } +} + +/// Parse \p Expr for a binary operation. The left operand of the operand is +/// given in \p Opl. Return the class representing that binary operation in +/// the AST of the numeric expression or nullptr if parsing in which case +/// errors are reported on \p SM. Also return in \p ImplicitFmt the implicit +/// matching format of the binary operation and in \p KnownPhase the FileCheck +/// phase when the value of that operand is known. +std::shared_ptr<FileCheckNumExprAST> FileCheckPattern::ParseFileCheckBinop( + StringRef &Expr, std::shared_ptr<FileCheckNumExprAST> Opl, + const SourceMgr &SM, struct FileCheckNumExprFmtType &ImplicitFmt, + enum Check::FileCheckPhase &KnownPhase) const { + SkipWhitespace(Expr); + if (Expr.empty()) + return Opl; + + // Check if this is a supported operation and selection function to perform + // it. + SMLoc oploc = SMLoc::getFromPointer(Expr.data()); + char Operator = Next(Expr); + binop_eval_t EvalBinop; + switch (Operator) { + case '+': + EvalBinop = FileCheckNumExprVal::Add; + break; + case '-': + EvalBinop = FileCheckNumExprVal::Sub; + break; + default: + SM.PrintMessage(oploc, SourceMgr::DK_Error, + std::string("Unsupported numeric operation '") + Operator + + "'"); + return nullptr; + } + + SkipWhitespace(Expr); + if (Expr.empty()) + return nullptr; + + // Parse right operand. + struct FileCheckNumExprFmtType OplImplicitFmt = ImplicitFmt; + enum Check::FileCheckPhase OplKnownPhase = KnownPhase; + std::shared_ptr<FileCheckNumExprAST> Opr = + ParseNumericOperand(Expr, SM, ImplicitFmt, KnownPhase); + if (Opr == nullptr) + return nullptr; + + // Compute implicit matching format of the binary operation and indicate + // whether the two operands have conflicting implicit format. + if (OplImplicitFmt.Set) { + if (ImplicitFmt.Set && OplImplicitFmt != ImplicitFmt) + ImplicitFmt.Conflict = 1; + else + ImplicitFmt = OplImplicitFmt; + } + + KnownPhase = OplKnownPhase > KnownPhase ? OplKnownPhase : KnownPhase; + return std::make_shared<FileCheckASTBinop>(EvalBinop, Opl, Opr); +} + +/// Parse \p Expr for a numeric expression. Return the class representing the +/// AST of numeric expression or nullptr if parsing fails in which case errors +/// are reported on \p SM. Set \p NumVarDef to the pointer to the class +/// representing the variable defined to this numeric expression if any. +std::shared_ptr<FileCheckNumExpr> FileCheckPattern::ParseNumericExpression( + StringRef Expr, std::shared_ptr<FileCheckNumExprVar> &NumVarDef, + const SourceMgr &SM) const { + struct FileCheckNumExprFmtType Fmt, ExplicitFmt = FmtNone; + + // Parse format specifier. + size_t FmtSpecEnd = Expr.find(','); + if (FmtSpecEnd != StringRef::npos) { + SkipWhitespace(Expr); + if (Skip(Expr, "%", false /*Optional*/)) { + SM.PrintMessage( + SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "Invalid matching format specification in numeric expression"); + return nullptr; + } + + // Check for unknown matching format specifier and set matching format in + // class instance representing this numeric expression. + SMLoc fmtloc = SMLoc::getFromPointer(Expr.data()); + switch (Next(Expr)) { + case 'u': + ExplicitFmt = FmtUnsigned; + break; + case 'd': + ExplicitFmt = FmtSigned; + break; + case 'x': + ExplicitFmt = FmtLowHex; + break; + case 'X': + ExplicitFmt = FmtCapHex; + break; + default: + SM.PrintMessage(fmtloc, SourceMgr::DK_Error, + "Invalid format specifier in numeric expression"); + return nullptr; + } + + SkipWhitespace(Expr); + if (Skip(Expr, ",", false /*Optional*/)) { + SM.PrintMessage( + SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "Invalid matching format specification in numeric expression"); + return nullptr; + } + } + + // Parse numeric variable definition. + NumVarDef.reset(); + size_t DefEnd = Expr.find(':'); + if (DefEnd != StringRef::npos) { + SkipWhitespace(Expr); + + NumVarDef = ParseNumericVariable(Expr, true /*IsDefinition*/, SM); + // Invalid variable definition. Error reporting done in parsing function. + if (NumVarDef == nullptr) + return nullptr; + + SkipWhitespace(Expr); + if (Skip(Expr, ":", false /*Optional*/)) + return nullptr; + SkipWhitespace(Expr); + } + + // Parse matching constraint. + SkipWhitespace(Expr); + if (Skip(Expr, "==", true /*Optional*/)) + return nullptr; + + // Parse numeric expression itself. + std::shared_ptr<FileCheckNumExprAST> NumExprAST; + struct FileCheckNumExprFmtType ImplicitFmt = FmtNone; + enum Check::FileCheckPhase KnownPhase; + if (Expr.empty()) + KnownPhase = Check::CheckPhase; + else { + SkipWhitespace(Expr); + NumExprAST = ParseNumericOperand(Expr, SM, ImplicitFmt, KnownPhase); + while (NumExprAST != nullptr && !Expr.empty()) + NumExprAST = + ParseFileCheckBinop(Expr, NumExprAST, SM, ImplicitFmt, KnownPhase); + if (NumExprAST == nullptr) + return nullptr; + } + + // Select explicit matching format if any, implicit one otherwise. Error out + // in case of conflicting implicit format without explicit format. + if (ExplicitFmt.Set) + Fmt = ExplicitFmt; + else if (ImplicitFmt.Conflict) { + SM.PrintMessage( + SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "Variables with conflicting format specifier: need an explicit one"); + return nullptr; + } else + Fmt = ImplicitFmt; + + auto NumExpr = + std::make_shared<FileCheckNumExpr>(NumExprAST, Fmt, KnownPhase); + if (NumVarDef != nullptr) + NumVarDef->SetNumExpr(NumExpr); + return NumExpr; +} + +/// Parse legacy numeric expressions in \p Expr, ie. numeric expressions that +/// were allowed using the pattern variable syntax before numeric expressions +/// support was added to FileCheck. Pass \p Name of pseudovariable used in the +/// expression and pass in \p Trailer the rest of the expression. If parsing +/// fails, errors are reported on \p SM. +std::shared_ptr<FileCheckNumExpr> +FileCheckPattern::ParseLegacyNumericExpression(StringRef Expr, StringRef Name, + StringRef Trailer, + const SourceMgr &SM) const { + if (!Name.equals("@LINE")) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "invalid pseudo variable in named regex"); + return nullptr; + } + + if (!Trailer.empty()) { + if (Trailer[0] != '+' && Trailer[0] != '-') { + SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), + SourceMgr::DK_Error, + "invalid operator for pseudo variable in named regex"); + return nullptr; + } + Trailer = Trailer.substr(1); + + int Offset; + if (Trailer.getAsInteger(10, Offset)) { + SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), + SourceMgr::DK_Error, + "invalid offset for pseudo variable in named regex"); + return nullptr; + } + } + + std::shared_ptr<FileCheckNumExprVar> NumVarDef; + std::shared_ptr<FileCheckNumExpr> NumExpr = + ParseNumericExpression(Expr, NumVarDef, SM); + assert(NumVarDef == nullptr && "Legacy numeric expression sets variable"); + return NumExpr; +} + /// Parses the given string into the Pattern. /// /// \p Prefix provides which prefix is being matched, \p SM provides the @@ -39,6 +862,14 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, this->LineNumber = LineNumber; PatternLoc = SMLoc::getFromPointer(PatternStr.data()); + // Create fake @LINE pseudo variable definition. + StringRef LinePseudo = "@LINE"; + uint64_t LineNumber64 = LineNumber; + auto LineNumberVal = FileCheckNumExprVal(LineNumber64); + auto LinePseudoVar = std::make_shared<FileCheckNumExprVar>( + LinePseudo, LineNumberVal, FmtUnsigned, LineNumber); + Context->GlobalNumericVariableTable[LinePseudo] = LinePseudoVar; + if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines)) // Ignore trailing whitespace. while (!PatternStr.empty() && @@ -118,92 +949,215 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, // itself must be of the form "[a-zA-Z_][0-9a-zA-Z_]*", otherwise we reject // it. This is to catch some common errors. if (PatternStr.startswith("[[")) { + StringRef MatchStr = PatternStr.substr(2); + bool IsNumExpr = MatchStr.consume_front("#"); + const char *RefTypeStr = IsNumExpr ? "numeric expression" : "named regex"; // Find the closing bracket pair ending the match. End is going to be an // offset relative to the beginning of the match string. - size_t End = FindRegexVarEnd(PatternStr.substr(2), SM); + size_t End = FindRegexVarEnd(MatchStr, SM); if (End == StringRef::npos) { - SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()), - SourceMgr::DK_Error, - "invalid named regex reference, no ]] found"); + SM.PrintMessage( + SMLoc::getFromPointer(PatternStr.data()), SourceMgr::DK_Error, + std::string("invalid ") + RefTypeStr + " reference, no ]] found"); return true; } - StringRef MatchStr = PatternStr.substr(2, End); - PatternStr = PatternStr.substr(End + 4); + MatchStr = MatchStr.substr(0, End); + PatternStr = PatternStr.substr(End + 4 + (int)IsNumExpr); + + bool CaptureParenNeeded; + bool SubstNeeded; + bool MatchSubstNeeded; + bool IsVarDef; + StringRef DefName; + StringRef SubstStr; + StringRef MatchRegexp; + unsigned SubstInsertIdx = RegExStr.size(); + std::shared_ptr<FileCheckNumExprVar> NumVarDef; + std::shared_ptr<FileCheckNumExpr> NumExpr; + + // Parse pattern variable or legacy numeric expression. + if (!IsNumExpr) { + // Get the regex name (e.g. "foo") and verify it is well formed. + bool IsPseudo; + unsigned TrailIdx; + if (ParseVariable(MatchStr, IsPseudo, TrailIdx)) { + SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()), + SourceMgr::DK_Error, "invalid name in named regex"); + return true; + } - // Get the regex name (e.g. "foo"). - size_t NameEnd = MatchStr.find(':'); - StringRef Name = MatchStr.substr(0, NameEnd); + StringRef Name = MatchStr.substr(0, TrailIdx); + StringRef Trailer = MatchStr.substr(TrailIdx); + size_t DefSepIdx = Trailer.find(":"); + IsVarDef = (DefSepIdx != StringRef::npos); + MatchSubstNeeded = !IsVarDef; + SubstNeeded = MatchSubstNeeded; + CaptureParenNeeded = IsVarDef; - if (Name.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, - "invalid name in named regex: empty name"); - return true; - } + if (IsVarDef && (IsPseudo || !Trailer.consume_front(":"))) { + SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()), + SourceMgr::DK_Error, + "invalid name in named regex definition"); + return true; + } - // Verify that the name/expression is well formed. FileCheck currently - // supports @LINE, @LINE+number, @LINE-number expressions. The check here - // is relaxed, more strict check is performed in \c EvaluateExpression. - bool IsExpression = false; - for (unsigned i = 0, e = Name.size(); i != e; ++i) { - if (i == 0) { - if (Name[i] == '$') // Global vars start with '$' - continue; - if (Name[i] == '@') { - if (NameEnd != StringRef::npos) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data()), - SourceMgr::DK_Error, - "invalid name in named regex definition"); + if (IsVarDef) { + // Detect collision between pattern and numeric variable when the + // former is created on a later line than the latter. + if (Context->GlobalNumericVariableTable.find(Name) != + Context->GlobalNumericVariableTable.end()) { + SM.PrintMessage( + SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error, + "Numeric variable with same name '" + Name + "' exists"); + return true; + } + DefName = Name; + MatchRegexp = Trailer; + } else { + SubstStr = MatchStr; + + // Syntax for pattern variables allowed for simple expressions based + // on @LINE pseudo variable (aka legacy numeric expressions) before + // numeric expression support was added. Accept those (and only + // those) for compatibility. + if (IsPseudo) { + NumExpr = ParseLegacyNumericExpression(MatchStr, Name, Trailer, SM); + if (NumExpr == nullptr) return true; - } - IsExpression = true; - continue; + IsNumExpr = true; + } else if (!Trailer.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), + SourceMgr::DK_Error, + "invalid use of operator in named regex"); + return true; } } - if (Name[i] != '_' && !isalnum(Name[i]) && - (!IsExpression || (Name[i] != '+' && Name[i] != '-'))) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data() + i), - SourceMgr::DK_Error, "invalid name in named regex"); + // Parse numeric expression. + } else { + SubstStr = MatchStr; + NumExpr = ParseNumericExpression(SubstStr, NumVarDef, SM); + if (NumExpr == nullptr) + return true; + } + + // Process numeric expressions and legacy numeric expressions. + if (IsNumExpr) { + IsVarDef = (NumVarDef != nullptr); + if (IsVarDef) + DefName = NumVarDef->GetName(); + + // Evaluate the numeric expression if we can already do it so that we + // directly add it to the pattern to match and don't have to do a + // substitution later. + if (NumExpr->GetKnownPhase() == Check::ParsePhase && + NumExpr->SetValueFromEval()) { + SM.PrintMessage( + SMLoc::getFromPointer(SubstStr.data()), SourceMgr::DK_Error, + "Failed to evaluate numeric expression known at parse time"); return true; } + + // Expression can be evaluated at match time, it will need a + // substitution. + MatchSubstNeeded = (NumExpr->GetKnownPhase() == Check::MatchPhase); + SubstNeeded = (NumExpr->GetAST() != nullptr); + + // Expression will only be known after matching is done, matched value + // needs to be captured and verified against the constraint of the + // numeric expression. + if (NumExpr->GetKnownPhase() > Check::MatchPhase) { + CaptureParenNeeded = true; + FileCheckNumExprMatch NumExprMatch = {NumExpr, CurParen}; + CapturedNumericExpressions.emplace_back(NumExprMatch); + // No capture needed otherwise. + } else + CaptureParenNeeded = false; + + // Get the string we need to match if we know it already. This could + // be either the value if we already know it or a suitable wildcard + // pattern if value would be known after match and needs to be verified + // against the constraint of the numeric expression. Numeric + // expression whose evaluation is known at match time get an empty + // string and a substitution is created instead. + std::string MatchString; + if (NumExpr->GetMatchString(Check::ParsePhase, MatchString)) + assert(false && "Failed to get string to match"); + MatchRegexp = StringRef(MatchString); } - // Name can't start with a digit. - if (isdigit(static_cast<unsigned char>(Name[0]))) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, - "invalid name in named regex"); - return true; + // Capturing parentheses are needed. Numeric expressions using variable + // defined on the same line need it to do the match in a single pass: + // they are matched using an appropriate wildcard pattern and the matched + // value gets verified against the constraint of the numeric expression + // later. + if (CaptureParenNeeded) { + RegExStr += '('; + // Checking numeric expressions requires substituting them by suitable + // wildcard patterns and capturing what these matched to. Adjust + // substitution index to be inside the capturing parentheses. + ++SubstInsertIdx; } - // Handle [[foo]]. - if (NameEnd == StringRef::npos) { - // Handle variables that were defined earlier on the same line by - // emitting a backreference. - if (VariableDefs.find(Name) != VariableDefs.end()) { - unsigned VarParenNum = VariableDefs[Name]; - if (VarParenNum < 1 || VarParenNum > 9) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data()), - SourceMgr::DK_Error, - "Can't back-reference more than 9 variables"); - return true; - } - AddBackrefToRegEx(VarParenNum); - } else { - VariableUses.push_back(std::make_pair(Name, RegExStr.size())); + // Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]]. + if (IsVarDef) { + if (IsNumExpr) + // This store is done here rather than in Match() to allow + // ParseNumericVariable() to get the pointer to the AST class + // instance of the right variable definition corresponding to a + // given numeric variable use. + Context->GlobalNumericVariableTable[DefName] = NumVarDef; + else { + VariableDefs[DefName] = CurParen; + // Mark pattern variable as defined to detect collision between + // pattern and numeric variable in ParseNumericVariable when the + // latter is created on a later line than the former. We cannot + // reuse GlobalVariableTable by populating it with an empty string + // for that since we would then loose the ability to detect use of + // undefined variable in Match(). + Context->DefinedVariableTable[DefName] = true; } - continue; } - // Handle [[foo:.*]]. - VariableDefs[Name] = CurParen; - RegExStr += '('; - ++CurParen; + if (CaptureParenNeeded) + ++CurParen; - if (AddRegExToRegEx(MatchStr.substr(NameEnd + 1), CurParen, SM)) + if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM)) return true; - RegExStr += ')'; + if (CaptureParenNeeded) + RegExStr += ')'; + + // Handle use of pattern variables that were defined earlier on the + // same line by emitting a backreference. Numeric expressions cannot + // be expressed using regexs so are recorded as substitutions to + // perform by FileCheck in the matching phase when only using variables + // defined on the line. See Match() for how numeric expressions using + // any variable defined on the same line are matched. + if (!IsNumExpr && VariableDefs.find(SubstStr) != VariableDefs.end()) { + unsigned CaptureParen = VariableDefs[SubstStr]; + if (CaptureParen < 1 || CaptureParen > 9) { + SM.PrintMessage(SMLoc::getFromPointer(SubstStr.data()), + SourceMgr::DK_Error, + "Can't back-reference more than 9 variables"); + return true; + } + AddBackrefToRegEx(CaptureParen); + // Handle use of pattern variables ([[<var>]]) defined in previous CHECK + // pattern or use of a numeric expression. + } else if (SubstNeeded) { + FileCheckPatternSubst Subst = + IsNumExpr + ? FileCheckPatternSubst(Context, SubstStr, NumExpr, + SubstInsertIdx) + : FileCheckPatternSubst(Context, SubstStr, SubstInsertIdx); + AllSubsts.push_back(Subst); + // Handle use of a numeric expression with all variables defined in + // previous CHECK pattern. + if (MatchSubstNeeded) + MatchSubsts.push_back(Subst); + } } // Handle fixed string matches. @@ -243,37 +1197,20 @@ void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) { RegExStr += Backref; } -/// Evaluates expression and stores the result to \p Value. -/// -/// Returns true on success and false when the expression has invalid syntax. -bool FileCheckPattern::EvaluateExpression(StringRef Expr, std::string &Value) const { - // The only supported expression is @LINE([\+-]\d+)? - if (!Expr.startswith("@LINE")) - return false; - Expr = Expr.substr(StringRef("@LINE").size()); - int Offset = 0; - if (!Expr.empty()) { - if (Expr[0] == '+') - Expr = Expr.substr(1); - else if (Expr[0] != '-') - return false; - if (Expr.getAsInteger(10, Offset)) - return false; - } - Value = llvm::itostr(LineNumber + Offset); - return true; -} - /// Matches the pattern string against the input buffer \p Buffer /// /// This returns the position that is matched or npos if there is no match. If /// there is a match, the size of the matched string is returned in \p /// MatchLen. /// -/// The \p VariableTable StringMap provides the current values of filecheck -/// variables and is updated if this match defines new values. +/// The GlobalVariableTable StringMap provides the current values of FileCheck +/// pattern variables and is updated if this match defines new values. +/// Substitutions for numeric expression point to the class instance +/// representing the numeric expression which is updated when the value is +/// known, either in this function or in ParsePattern if their value is known +/// at parse time. size_t FileCheckPattern::Match(StringRef Buffer, size_t &MatchLen, - StringMap<StringRef> &VariableTable) const { + const SourceMgr &SM) const { // If this is the EOF pattern, match it immediately. if (CheckTy == Check::CheckEOF) { MatchLen = 0; @@ -292,29 +1229,43 @@ size_t FileCheckPattern::Match(StringRef Buffer, size_t &MatchLen, // actual value. StringRef RegExToMatch = RegExStr; std::string TmpStr; - if (!VariableUses.empty()) { + SmallVector<StringRef, 4> MatchInfo; + + if (!MatchSubsts.empty()) { TmpStr = RegExStr; unsigned InsertOffset = 0; - for (const auto &VariableUse : VariableUses) { + // Substitute all pattern variables and numeric expressions whose value is + // known just now. Use of pattern variables defined on the same line are + // handled by back-references. Numeric expressions whose value is known at + // parse time (eg. uses of @LINE pseudo variable) are replaced at parse + // time. For numeric expressions using variable defined on the same line, + // RegExStr is filled with a suitable wildcard pattern and the constraint + // is then verified against the matched value. + // + // Known issue: Due to the constraints for numeric expressions using + // variable defined in earlier line not being expressed at the regular + // expression level, the wrong values might be matched, leading to a + // constraint check failure. An example of this is what happens when + // checking the line "1 3 4" against the directive + // "CHECK: [[#N:]] [[#N+1]]" which gets substituted by + // "[[:digit:]]+ [[:digit:]]+". This would match 1 3 which then fails the + // constraint checks. + // + // One possible solution would be to augment the regular expression engine + // to be able to return all matches for a given pattern. FileCheck could + // then test all possibility and fail the check only if none of them + // satisfy all numeric expressions. This would work both for numeric + // expression using a numeric variable defined on the same line as well as + // numeric expressions with a comparison rather than equality constraint. + for (const auto &MatchSubst : MatchSubsts) { + // Substitute and check for failure (eg. use of undefined variable). std::string Value; - - if (VariableUse.first[0] == '@') { - if (!EvaluateExpression(VariableUse.first, Value)) - return StringRef::npos; - } else { - StringMap<StringRef>::iterator it = - VariableTable.find(VariableUse.first); - // If the variable is undefined, return an error. - if (it == VariableTable.end()) - return StringRef::npos; - - // Look up the value and escape it so that we can put it into the regex. - Value += Regex::escape(it->second); - } + if (MatchSubst.Substitute(Value)) + return StringRef::npos; // Plop it into the regex at the adjusted offset. - TmpStr.insert(TmpStr.begin() + VariableUse.second + InsertOffset, + TmpStr.insert(TmpStr.begin() + MatchSubst.GetIndex() + InsertOffset, Value.begin(), Value.end()); InsertOffset += Value.size(); } @@ -323,35 +1274,94 @@ size_t FileCheckPattern::Match(StringRef Buffer, size_t &MatchLen, RegExToMatch = TmpStr; } - SmallVector<StringRef, 4> MatchInfo; - if (!Regex(RegExToMatch, Regex::Newline).match(Buffer, &MatchInfo)) - return StringRef::npos; + bool AllNumExprSatisfied; + do { + AllNumExprSatisfied = true; + if (!Regex(RegExToMatch, Regex::Newline).match(Buffer, &MatchInfo)) + return StringRef::npos; - // Successful regex match. - assert(!MatchInfo.empty() && "Didn't get any match"); - StringRef FullMatch = MatchInfo[0]; + // Successful regex match. + assert(!MatchInfo.empty() && "Didn't get any match"); - // If this defines any variables, remember their values. - for (const auto &VariableDef : VariableDefs) { - assert(VariableDef.second < MatchInfo.size() && "Internal paren error"); - VariableTable[VariableDef.first] = MatchInfo[VariableDef.second]; - } + // If this defines any pattern variables, remember their values. + for (const auto &VariableDef : VariableDefs) { + StringRef Name = VariableDef.first; + unsigned CaptureParen = VariableDef.second; + assert(CaptureParen < MatchInfo.size() && "Internal paren error"); + StringRef MatchedStr = MatchInfo[CaptureParen]; + Context->GlobalVariableTable[Name] = MatchedStr; + } + + // Check the matched value satisfies the numeric expression constraint and + // store it in the numeric expression if that's the case. + for (const auto &CapturedNumericExpression : CapturedNumericExpressions) { + assert(CapturedNumericExpression.CaptureParen < MatchInfo.size() && + "Internal paren error"); + StringRef MatchedStr = MatchInfo[CapturedNumericExpression.CaptureParen]; + FileCheckNumExpr *NumExpr = CapturedNumericExpression.NumExpr.get(); + + // If numeric expression can be evaluated now, do it and error out if + // evaluation fails. + bool UnconstrainedVarDef = (NumExpr->GetAST() == nullptr); + if (!UnconstrainedVarDef) { + if (NumExpr->SetValueFromEval()) + return StringRef::npos; + } + + // Get numeric value from the matched string according to matching format + // of the numeric expression. Error out if this fails. + FileCheckNumExprVal MatchedVal; + if (NumExpr->ValueFromStringRepr(MatchedStr, MatchedVal)) { + SM.PrintMessage(SMLoc::getFromPointer(MatchedStr.data()), + SourceMgr::DK_Error, + "Cannot represent numeric value " + MatchedStr); + return StringRef::npos; + } + + // Now verify the constraint of the numeric expression. + if (NumExpr->VerifyConstraint(MatchedVal)) { + AllNumExprSatisfied = false; + // Try matching again from the line following the one matched. There + // might be over match in the current line but we have no way to ask + // for them to the regular expression engine. However this hazard can + // be alleviated if the user write a CHECK pattern that has numeric + // expression for all numeric values the line to match contains. + Buffer = Buffer.substr(Buffer.find_first_of('\n')).substr(1); + break; + } + + // Store verified matched value. This will set variable defined by the + // expression if any. + if (UnconstrainedVarDef) { + if (NumExpr->SetTentativeValue(MatchedVal)) + assert(false && "Value of numeric expression already set"); + } + } + } while (!AllNumExprSatisfied && !Buffer.empty()); + + // All constraints satisfied, make variable definitions final. + if (AllNumExprSatisfied) { + for (const auto &CapturedNumericExpression : CapturedNumericExpressions) { + FileCheckNumExpr *NumExpr = CapturedNumericExpression.NumExpr.get(); + NumExpr->CommitValue(); + } + // No line with all constraints satisfied found, return failure to match. + } else + return StringRef::npos; // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after // the required preceding newline, which is consumed by the pattern in the // case of CHECK-EMPTY but not CHECK-NEXT. size_t MatchStartSkip = CheckTy == Check::CheckEmpty; + StringRef FullMatch = MatchInfo[0]; MatchLen = FullMatch.size() - MatchStartSkip; return FullMatch.data() - Buffer.data() + MatchStartSkip; } - /// Computes an arbitrary estimate for the quality of matching this pattern at /// the start of \p Buffer; a distance of zero should correspond to a perfect /// match. -unsigned -FileCheckPattern::ComputeMatchDistance(StringRef Buffer, - const StringMap<StringRef> &VariableTable) const { +unsigned FileCheckPattern::ComputeMatchDistance(StringRef Buffer) const { // Just compute the number of matching characters. For regular expressions, we // just compare against the regex itself and hope for the best. // @@ -368,38 +1378,50 @@ FileCheckPattern::ComputeMatchDistance(StringRef Buffer, return BufferPrefix.edit_distance(ExampleString); } -void FileCheckPattern::PrintVariableUses(const SourceMgr &SM, StringRef Buffer, - const StringMap<StringRef> &VariableTable, - SMRange MatchRange) const { - // If this was a regular expression using variables, print the current - // variable values. - if (!VariableUses.empty()) { - for (const auto &VariableUse : VariableUses) { +/// Print value of successful substitutions or name of undefined pattern or +/// numeric variables preventing such a successful substitution. +/// +/// Note: Numeric expressions known at parse time are not shown here since no +/// substitution gets created for them but hopefully if the value is known at +/// parse time the value should be obvious (except in case of bug in FileCheck +/// itself). Numeric expression using numeric variables defined on the same +/// line are not shown either. +void FileCheckPattern::PrintSubsts(const SourceMgr &SM, StringRef Buffer, + SMRange MatchRange) const { + // Print info what we know about substitutions. This covers both uses of + // pattern variables and numeric subsitutions as long as they only use + // variables defined in earlier lines. + if (!AllSubsts.empty()) { + for (const auto &Subst : AllSubsts) { SmallString<256> Msg; raw_svector_ostream OS(Msg); - StringRef Var = VariableUse.first; - if (Var[0] == '@') { - std::string Value; - if (EvaluateExpression(Var, Value)) { - OS << "with expression \""; - OS.write_escaped(Var) << "\" equal to \""; - OS.write_escaped(Value) << "\""; - } else { - OS << "uses incorrect expression \""; - OS.write_escaped(Var) << "\""; + bool IsNumExpr = Subst.IsNumExpr(); + std::string Val; + + // Substitution failed or is not known at match time, print undefined + // variables it uses. + if (Subst.MatchedValue(Val)) { + std::vector<StringRef> UndefVarNames; + Subst.GetUndefVarNames(UndefVarNames); + if (UndefVarNames.empty()) + continue; + if (IsNumExpr) + OS << "uses undefined numeric variables"; + else + OS << "uses undefined variable"; + for (auto UndefVarName : UndefVarNames) { + OS << " \""; + OS.write_escaped(UndefVarName) << "\""; } + // Substitution succeeded and numeric expression satisfied its + // constraint. Print substituted value. } else { - StringMap<StringRef>::const_iterator it = VariableTable.find(Var); - - // Check for undefined variable references. - if (it == VariableTable.end()) { - OS << "uses undefined variable \""; - OS.write_escaped(Var) << "\""; - } else { + if (IsNumExpr) + OS << "with numeric expression \""; + else OS << "with variable \""; - OS.write_escaped(Var) << "\" equal to \""; - OS.write_escaped(it->second) << "\""; - } + OS.write_escaped(Subst.GetSubstString()) << "\" equal to \""; + OS.write_escaped(Val) << "\""; } if (MatchRange.isValid()) @@ -432,7 +1454,6 @@ static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy, void FileCheckPattern::PrintFuzzyMatch( const SourceMgr &SM, StringRef Buffer, - const StringMap<StringRef> &VariableTable, std::vector<FileCheckDiag> *Diags) const { // Attempt to find the closest/best fuzzy match. Usually an error happens // because some string in the output didn't exactly match. In these cases, we @@ -454,7 +1475,7 @@ void FileCheckPattern::PrintFuzzyMatch( // Compute the "quality" of this match as an arbitrary combination of the // match distance and the number of lines skipped to get to this match. - unsigned Distance = ComputeMatchDistance(Buffer.substr(i), VariableTable); + unsigned Distance = ComputeMatchDistance(Buffer.substr(i)); double Quality = Distance + (NumLinesForward / 100.); if (Quality < BestQuality || Best == StringRef::npos) { @@ -478,6 +1499,18 @@ void FileCheckPattern::PrintFuzzyMatch( } } +/// Store in \p VarValue the matched value for pattern variable \p VarName if +/// defined. Return whether \p VarName is undefined. +bool FileCheckPatternContext::GetPatternVarValue(StringRef VarName, + StringRef &VarValue) { + auto git = GlobalVariableTable.find(VarName); + if (git == GlobalVariableTable.end()) + return true; + + VarValue = git->second; + return false; +} + /// Finds the closing sequence of a regex variable usage or definition. /// /// \p Str has to point in the beginning of the definition (right after the @@ -748,9 +1781,15 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer, /// /// The strings are added to the CheckStrings vector. Returns true in case of /// an error, false otherwise. -bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, - Regex &PrefixRE, - std::vector<FileCheckString> &CheckStrings) { +bool llvm::FileCheck::ReadCheckFile( + SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, + std::vector<FileCheckString> &CheckStrings) { + auto *PatternContext = new FileCheckPatternContext(); + // Process command-line definition of variables at parse time so that numeric + // expression can refer to numeric variable defined on the command-line. + if (PatternContext->DefineCmdlineVariables(Req.GlobalDefines, SM)) + return true; + std::vector<FileCheckPattern> ImplicitNegativeChecks; for (const auto &PatternString : Req.ImplicitCheckNot) { // Create a buffer with fake command line content in order to display the @@ -764,7 +1803,8 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, CmdLine->getBuffer().substr(Prefix.size(), PatternString.size()); SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); - ImplicitNegativeChecks.push_back(FileCheckPattern(Check::CheckNot)); + ImplicitNegativeChecks.push_back( + FileCheckPattern(Check::CheckNot, PatternContext)); ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer, "IMPLICIT-CHECK", SM, 0, Req); } @@ -827,7 +1867,7 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); // Parse the pattern. - FileCheckPattern P(CheckTy); + FileCheckPattern P(CheckTy, PatternContext); if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber, Req)) return true; @@ -871,7 +1911,8 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first // prefix as a filler for the error message. if (!DagNotMatches.empty()) { - CheckStrings.emplace_back(FileCheckPattern(Check::CheckEOF), *Req.CheckPrefixes.begin(), + CheckStrings.emplace_back(FileCheckPattern(Check::CheckEOF, PatternContext), + *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data())); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } @@ -897,8 +1938,7 @@ bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat, - int MatchedCount, StringRef Buffer, - StringMap<StringRef> &VariableTable, size_t MatchPos, + int MatchedCount, StringRef Buffer, size_t MatchPos, size_t MatchLen, const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) { if (ExpectedMatch) { @@ -922,24 +1962,22 @@ static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message); SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here", {MatchRange}); - Pat.PrintVariableUses(SM, Buffer, VariableTable, MatchRange); + Pat.PrintSubsts(SM, Buffer, MatchRange); } static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, const FileCheckString &CheckStr, int MatchedCount, - StringRef Buffer, StringMap<StringRef> &VariableTable, - size_t MatchPos, size_t MatchLen, FileCheckRequest &Req, + StringRef Buffer, size_t MatchPos, size_t MatchLen, + FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) { PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, - MatchedCount, Buffer, VariableTable, MatchPos, MatchLen, Req, - Diags); + MatchedCount, Buffer, MatchPos, MatchLen, Req, Diags); } static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat, int MatchedCount, - StringRef Buffer, StringMap<StringRef> &VariableTable, - bool VerboseVerbose, + StringRef Buffer, bool VerboseVerbose, std::vector<FileCheckDiag> *Diags) { if (!ExpectedMatch && !VerboseVerbose) return; @@ -965,19 +2003,18 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here"); // Allow the pattern to print additional information if desired. - Pat.PrintVariableUses(SM, Buffer, VariableTable); + Pat.PrintSubsts(SM, Buffer); if (ExpectedMatch) - Pat.PrintFuzzyMatch(SM, Buffer, VariableTable, Diags); + Pat.PrintFuzzyMatch(SM, Buffer, Diags); } static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, const FileCheckString &CheckStr, int MatchedCount, - StringRef Buffer, StringMap<StringRef> &VariableTable, - bool VerboseVerbose, + StringRef Buffer, bool VerboseVerbose, std::vector<FileCheckDiag> *Diags) { PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, - MatchedCount, Buffer, VariableTable, VerboseVerbose, Diags); + MatchedCount, Buffer, VerboseVerbose, Diags); } /// Count the number of newlines in the specified range. @@ -1006,7 +2043,6 @@ static unsigned CountNumNewlinesBetween(StringRef Range, /// Match check string and its "not strings" and/or "dag strings". size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode, size_t &MatchLen, - StringMap<StringRef> &VariableTable, FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const { size_t LastPos = 0; @@ -1018,7 +2054,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer, // over the block again (including the last CHECK-LABEL) in normal mode. if (!IsLabelScanMode) { // Match "dag strings" (with mixed "not strings" if any). - LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable, Req, Diags); + LastPos = CheckDag(SM, Buffer, NotStrings, Req, Diags); if (LastPos == StringRef::npos) return StringRef::npos; } @@ -1033,18 +2069,17 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer, StringRef MatchBuffer = Buffer.substr(LastMatchEnd); size_t CurrentMatchLen; // get a match at current start point - size_t MatchPos = Pat.Match(MatchBuffer, CurrentMatchLen, VariableTable); + size_t MatchPos = Pat.Match(MatchBuffer, CurrentMatchLen, SM); if (i == 1) FirstMatchPos = LastPos + MatchPos; // report if (MatchPos == StringRef::npos) { - PrintNoMatch(true, SM, *this, i, MatchBuffer, VariableTable, - Req.VerboseVerbose, Diags); + PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags); return StringRef::npos; } - PrintMatch(true, SM, *this, i, MatchBuffer, VariableTable, MatchPos, - CurrentMatchLen, Req, Diags); + PrintMatch(true, SM, *this, i, MatchBuffer, MatchPos, CurrentMatchLen, Req, + Diags); // move start point after the match LastMatchEnd += MatchPos + CurrentMatchLen; @@ -1079,7 +2114,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer, // If this match had "not strings", verify that they don't exist in the // skipped region. - if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags)) + if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags)) return StringRef::npos; } @@ -1165,22 +2200,21 @@ bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const { bool FileCheckString::CheckNot( const SourceMgr &SM, StringRef Buffer, const std::vector<const FileCheckPattern *> &NotStrings, - StringMap<StringRef> &VariableTable, const FileCheckRequest &Req, - std::vector<FileCheckDiag> *Diags) const { + const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const { for (const FileCheckPattern *Pat : NotStrings) { assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); size_t MatchLen = 0; - size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable); + size_t Pos = Pat->Match(Buffer, MatchLen, SM); if (Pos == StringRef::npos) { PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, - VariableTable, Req.VerboseVerbose, Diags); + Req.VerboseVerbose, Diags); continue; } - PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, VariableTable, - Pos, MatchLen, Req, Diags); + PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen, + Req, Diags); return true; } @@ -1192,7 +2226,6 @@ bool FileCheckString::CheckNot( size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, std::vector<const FileCheckPattern *> &NotStrings, - StringMap<StringRef> &VariableTable, const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const { if (DagNotStrings.empty()) @@ -1233,19 +2266,19 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, // CHECK-DAG group. for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) { StringRef MatchBuffer = Buffer.substr(MatchPos); - size_t MatchPosBuf = Pat.Match(MatchBuffer, MatchLen, VariableTable); + size_t MatchPosBuf = Pat.Match(MatchBuffer, MatchLen, SM); // With a group of CHECK-DAGs, a single mismatching means the match on // that group of CHECK-DAGs fails immediately. if (MatchPosBuf == StringRef::npos) { PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer, - VariableTable, Req.VerboseVerbose, Diags); + Req.VerboseVerbose, Diags); return StringRef::npos; } // Re-calc it as the offset relative to the start of the original string. MatchPos += MatchPosBuf; if (Req.VerboseVerbose) - PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, - VariableTable, MatchPos, MatchLen, Req, Diags); + PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos, + MatchLen, Req, Diags); MatchRange M{MatchPos, MatchPos + MatchLen}; if (Req.AllowDeprecatedDagOverlap) { // We don't need to track all matches in this mode, so we just maintain @@ -1288,8 +2321,8 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, MatchPos = MI->End; } if (!Req.VerboseVerbose) - PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, VariableTable, - MatchPos, MatchLen, Req, Diags); + PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, MatchPos, + MatchLen, Req, Diags); // Handle the end of a CHECK-DAG group. if (std::next(PatItr) == PatEnd || @@ -1300,7 +2333,7 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, // region. StringRef SkippedRegion = Buffer.slice(StartPos, MatchRanges.begin()->Pos); - if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags)) + if (CheckNot(SM, SkippedRegion, NotStrings, Req, Diags)) return StringRef::npos; // Clear "not strings". NotStrings.clear(); @@ -1364,16 +2397,103 @@ Regex llvm::FileCheck::buildCheckPrefixRegex() { return Regex(PrefixRegexStr); } -// Remove local variables from \p VariableTable. Global variables -// (start with '$') are preserved. -static void ClearLocalVars(StringMap<StringRef> &VariableTable) { - SmallVector<StringRef, 16> LocalVars; - for (const auto &Var : VariableTable) +/// Define pattern and numeric variables from definitions given on the command +/// line passed as a vector of VAR=VAL strings in \p CmdlineDefines. Report +/// any error to \p SM. +bool FileCheckPatternContext::DefineCmdlineVariables( + std::vector<std::string> &CmdlineDefines, SourceMgr &SM) { + static bool CmdlineVariablesDefined = false; + + if (CmdlineVariablesDefined) + return true; + CmdlineVariablesDefined = true; + + // Dummy pattern to call ParseNumericExpression + FileCheckPattern P(Check::CheckPlain, this); + + bool ErrorFound = false; + for (const auto &CmdlineDef : CmdlineDefines) { + // Numeric variable definition. Same format as numeric expression with + // variable definition where ':' is replaced by '=' + if (CmdlineDef[0] == '#') { + // Copy command-line definition text with leading '#' stripped and + // replace '=' by ':' to be able to reuse ParseNumericExpression. + // Create a buffer with fake command line content in order to display + // parsing diagnostic with location information and point to the + // command-line option with invalid syntax. + std::string Prefix = "-D"; + std::string Suffix1 = " (parsed as: [["; + std::string Suffix2 = CmdlineDef; + size_t EqIdx = Suffix2.find('='); + Suffix2[EqIdx] = ':'; + std::string Suffix3 = "]])"; + std::string DiagCmdline = + Prefix + CmdlineDef + Suffix1 + Suffix2 + Suffix3; + std::unique_ptr<MemoryBuffer> CmdLine = + MemoryBuffer::getMemBufferCopy(DiagCmdline, "command line"); + StringRef DiagCmdlineDefRef = CmdLine->getBuffer(); + SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + + // Now parse to both check syntax is correct and create the necessary + // class instance. + size_t NumExprStartIdx = + Prefix.size() + CmdlineDef.size() + Suffix1.size() + 1; + StringRef CmdlineDefRef = + DiagCmdlineDefRef.substr(NumExprStartIdx).drop_back(Suffix3.size()); + std::shared_ptr<FileCheckNumExprVar> NumVarDef; + std::shared_ptr<FileCheckNumExpr> NumExpr = + P.ParseNumericExpression(CmdlineDefRef, NumVarDef, SM); + if (NumVarDef == nullptr) { + ErrorFound = true; + continue; + } + NumExpr->SetValueFromEval(); + + // Record this variable definition. + GlobalNumericVariableTable[NumVarDef->GetName()] = NumVarDef; + // Pattern variable definition + } else { + std::string Prefix = "-D"; + std::string DiagCmdline = Prefix + CmdlineDef; + std::unique_ptr<MemoryBuffer> CmdLine = + MemoryBuffer::getMemBufferCopy(DiagCmdline, "command line"); + StringRef CmdlineDefRef = CmdLine->getBuffer().substr(Prefix.size()); + SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + + bool IsPseudo; + unsigned TrailIdx; + std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDefRef.split('='); + StringRef Name = CmdlineNameVal.first; + if (P.ParseVariable(Name, IsPseudo, TrailIdx) || IsPseudo || + TrailIdx != Name.size()) { + SM.PrintMessage(SMLoc::getFromPointer(CmdlineDefRef.data()), + SourceMgr::DK_Error, + "Invalid name for variable definition '" + Name + "'"); + ErrorFound = true; + continue; + } + GlobalVariableTable.insert(CmdlineDefRef.split('=')); + } + } + + return ErrorFound; +} + +/// Undefine local variables (variables whose name does not start with a '$' +/// sign), ie remove them from GlobalVariableTable. +void FileCheckPatternContext::ClearLocalVars(void) { + SmallVector<StringRef, 16> LocalPatternVars, LocalNumericVars; + for (const auto &Var : GlobalVariableTable) + if (Var.first()[0] != '$') + LocalPatternVars.push_back(Var.first()); + for (const auto &Var : GlobalNumericVariableTable) if (Var.first()[0] != '$') - LocalVars.push_back(Var.first()); + LocalNumericVars.push_back(Var.first()); - for (const auto &Var : LocalVars) - VariableTable.erase(Var); + for (const auto &Var : LocalPatternVars) + GlobalVariableTable.erase(Var); + for (const auto &Var : LocalNumericVars) + GlobalNumericVariableTable.erase(Var); } /// Check the input to FileCheck provided in the \p Buffer against the \p @@ -1385,12 +2505,8 @@ bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer, std::vector<FileCheckDiag> *Diags) { bool ChecksFailed = false; - /// VariableTable - This holds all the current filecheck variables. - StringMap<StringRef> VariableTable; - - for (const auto& Def : Req.GlobalDefines) - VariableTable.insert(StringRef(Def).split('=')); - + FileCheckPatternContext *Context = + CheckStrings.empty() ? nullptr : CheckStrings[0].Pat.GetContext(); unsigned i = 0, j = 0, e = CheckStrings.size(); while (true) { StringRef CheckRegion; @@ -1405,10 +2521,10 @@ bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer, // Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG size_t MatchLabelLen = 0; - size_t MatchLabelPos = CheckLabelStr.Check( - SM, Buffer, true, MatchLabelLen, VariableTable, Req, Diags); + size_t MatchLabelPos = + CheckLabelStr.Check(SM, Buffer, true, MatchLabelLen, Req, Diags); if (MatchLabelPos == StringRef::npos) - // Immediately bail of CHECK-LABEL fails, nothing else we can do. + // Immediately bail if CHECK-LABEL fails, nothing else we can do. return false; CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen); @@ -1416,8 +2532,8 @@ bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer, ++j; } - if (Req.EnableVarScope) - ClearLocalVars(VariableTable); + if (Req.EnableVarScope && Context != nullptr) + Context->ClearLocalVars(); for (; i != j; ++i) { const FileCheckString &CheckStr = CheckStrings[i]; @@ -1425,8 +2541,8 @@ bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer, // Check each string within the scanned region, including a second check // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG) size_t MatchLen = 0; - size_t MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen, - VariableTable, Req, Diags); + size_t MatchPos = + CheckStr.Check(SM, CheckRegion, false, MatchLen, Req, Diags); if (MatchPos == StringRef::npos) { ChecksFailed = true; @@ -1440,6 +2556,7 @@ bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer, if (j == e) break; } + delete Context; // Success if no checks failed. return !ChecksFailed; |