diff options
Diffstat (limited to 'lib/Support/FileCheck.cpp')
-rw-r--r-- | lib/Support/FileCheck.cpp | 356 |
1 files changed, 198 insertions, 158 deletions
diff --git a/lib/Support/FileCheck.cpp b/lib/Support/FileCheck.cpp index e0f17787bdf8..841e406a7b69 100644 --- a/lib/Support/FileCheck.cpp +++ b/lib/Support/FileCheck.cpp @@ -14,31 +14,22 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/FileCheck.h" +#include "FileCheckImpl.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/FormatVariadic.h" #include <cstdint> #include <list> -#include <map> #include <tuple> #include <utility> using namespace llvm; -void FileCheckNumericVariable::setValue(uint64_t NewValue) { - assert(!Value && "Overwriting numeric variable's value is not allowed"); - Value = NewValue; -} - -void FileCheckNumericVariable::clearValue() { - if (!Value) - return; - Value = None; -} - Expected<uint64_t> FileCheckNumericVariableUse::eval() const { Optional<uint64_t> Value = NumericVariable->getValue(); if (Value) return *Value; + return make_error<FileCheckUndefVarError>(Name); } @@ -109,7 +100,7 @@ FileCheckPattern::parseVariable(StringRef &Str, const SourceMgr &SM) { // StringRef holding all characters considered as horizontal whitespaces by // FileCheck input canonicalization. -StringRef SpaceChars = " \t"; +constexpr StringLiteral SpaceChars = " \t"; // Parsing helper function that strips the first character in S and returns it. static char popFront(StringRef &S) { @@ -159,7 +150,9 @@ FileCheckPattern::parseNumericVariableDefinition( Expected<std::unique_ptr<FileCheckNumericVariableUse>> FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo, - const SourceMgr &SM) const { + Optional<size_t> LineNumber, + FileCheckPatternContext *Context, + const SourceMgr &SM) { if (IsPseudo && !Name.equals("@LINE")) return FileCheckErrorDiagnostic::get( SM, Name, "invalid pseudo numeric variable '" + Name + "'"); @@ -185,21 +178,25 @@ FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo, if (DefLineNumber && LineNumber && *DefLineNumber == *LineNumber) return FileCheckErrorDiagnostic::get( SM, Name, - "numeric variable '" + Name + "' defined on the same line as used"); + "numeric variable '" + Name + + "' defined earlier in the same CHECK directive"); - return llvm::make_unique<FileCheckNumericVariableUse>(Name, NumericVariable); + return std::make_unique<FileCheckNumericVariableUse>(Name, NumericVariable); } Expected<std::unique_ptr<FileCheckExpressionAST>> FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO, - const SourceMgr &SM) const { + Optional<size_t> LineNumber, + FileCheckPatternContext *Context, + const SourceMgr &SM) { if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) { // Try to parse as a numeric variable use. Expected<FileCheckPattern::VariableProperties> ParseVarResult = parseVariable(Expr, SM); if (ParseVarResult) return parseNumericVariableUse(ParseVarResult->Name, - ParseVarResult->IsPseudo, SM); + ParseVarResult->IsPseudo, LineNumber, + Context, SM); if (AO == AllowedOperand::LineVar) return ParseVarResult.takeError(); // Ignore the error and retry parsing as a literal. @@ -209,7 +206,7 @@ FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO, // Otherwise, parse it as a literal. uint64_t LiteralValue; if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue)) - return llvm::make_unique<FileCheckExpressionLiteral>(LiteralValue); + return std::make_unique<FileCheckExpressionLiteral>(LiteralValue); return FileCheckErrorDiagnostic::get(SM, Expr, "invalid operand format '" + Expr + "'"); @@ -223,10 +220,10 @@ static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { return LeftOp - RightOp; } -Expected<std::unique_ptr<FileCheckExpressionAST>> -FileCheckPattern::parseBinop(StringRef &Expr, - std::unique_ptr<FileCheckExpressionAST> LeftOp, - bool IsLegacyLineExpr, const SourceMgr &SM) const { +Expected<std::unique_ptr<FileCheckExpressionAST>> FileCheckPattern::parseBinop( + StringRef &Expr, std::unique_ptr<FileCheckExpressionAST> LeftOp, + bool IsLegacyLineExpr, Optional<size_t> LineNumber, + FileCheckPatternContext *Context, const SourceMgr &SM) { Expr = Expr.ltrim(SpaceChars); if (Expr.empty()) return std::move(LeftOp); @@ -257,12 +254,12 @@ FileCheckPattern::parseBinop(StringRef &Expr, AllowedOperand AO = IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any; Expected<std::unique_ptr<FileCheckExpressionAST>> RightOpResult = - parseNumericOperand(Expr, AO, SM); + parseNumericOperand(Expr, AO, LineNumber, Context, SM); if (!RightOpResult) return RightOpResult; Expr = Expr.ltrim(SpaceChars); - return llvm::make_unique<FileCheckASTBinop>(EvalBinop, std::move(LeftOp), + return std::make_unique<FileCheckASTBinop>(EvalBinop, std::move(LeftOp), std::move(*RightOpResult)); } @@ -270,56 +267,60 @@ Expected<std::unique_ptr<FileCheckExpressionAST>> FileCheckPattern::parseNumericSubstitutionBlock( StringRef Expr, Optional<FileCheckNumericVariable *> &DefinedNumericVariable, - bool IsLegacyLineExpr, const SourceMgr &SM) const { - // Parse the numeric variable definition. + bool IsLegacyLineExpr, Optional<size_t> LineNumber, + FileCheckPatternContext *Context, const SourceMgr &SM) { + std::unique_ptr<FileCheckExpressionAST> ExpressionAST = nullptr; + StringRef DefExpr = StringRef(); DefinedNumericVariable = None; + // Save variable definition expression if any. size_t DefEnd = Expr.find(':'); if (DefEnd != StringRef::npos) { - StringRef DefExpr = Expr.substr(0, DefEnd); - StringRef UseExpr = Expr.substr(DefEnd + 1); + DefExpr = Expr.substr(0, DefEnd); + Expr = Expr.substr(DefEnd + 1); + } - UseExpr = UseExpr.ltrim(SpaceChars); - if (!UseExpr.empty()) - return FileCheckErrorDiagnostic::get( - SM, UseExpr, - "unexpected string after variable definition: '" + UseExpr + "'"); + // Parse the expression itself. + Expr = Expr.ltrim(SpaceChars); + if (!Expr.empty()) { + // The first operand in a legacy @LINE expression is always the @LINE + // pseudo variable. + AllowedOperand AO = + IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any; + Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult = + parseNumericOperand(Expr, AO, LineNumber, Context, SM); + while (ParseResult && !Expr.empty()) { + ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, + LineNumber, Context, SM); + // Legacy @LINE expressions only allow 2 operands. + if (ParseResult && IsLegacyLineExpr && !Expr.empty()) + return FileCheckErrorDiagnostic::get( + SM, Expr, + "unexpected characters at end of expression '" + Expr + "'"); + } + if (!ParseResult) + return ParseResult; + ExpressionAST = std::move(*ParseResult); + } + // Parse the numeric variable definition. + if (DefEnd != StringRef::npos) { DefExpr = DefExpr.ltrim(SpaceChars); Expected<FileCheckNumericVariable *> ParseResult = parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM); + if (!ParseResult) return ParseResult.takeError(); DefinedNumericVariable = *ParseResult; - - return nullptr; } - // Parse the expression itself. - Expr = Expr.ltrim(SpaceChars); - // The first operand in a legacy @LINE expression is always the @LINE pseudo - // variable. - AllowedOperand AO = - IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any; - Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult = - parseNumericOperand(Expr, AO, SM); - while (ParseResult && !Expr.empty()) { - ParseResult = - parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, SM); - // Legacy @LINE expressions only allow 2 operands. - if (ParseResult && IsLegacyLineExpr && !Expr.empty()) - return FileCheckErrorDiagnostic::get( - SM, Expr, - "unexpected characters at end of expression '" + Expr + "'"); - } - if (!ParseResult) - return ParseResult; - return std::move(*ParseResult); + return std::move(ExpressionAST); } bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, const FileCheckRequest &Req) { bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot; + IgnoreCase = Req.IgnoreCase; PatternLoc = SMLoc::getFromPointer(PatternStr.data()); @@ -396,14 +397,15 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, continue; } - // String and numeric substitution blocks. String substitution blocks come + // String and numeric substitution blocks. Pattern substitution blocks come // in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some // other regex) and assigns it to the string variable 'foo'. The latter - // substitutes foo's value. Numeric substitution blocks work the same way - // as string ones, but start with a '#' sign after the double brackets. - // Both string and numeric variable names must satisfy the regular - // expression "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch - // some common errors. + // substitutes foo's value. Numeric substitution blocks recognize the same + // form as string ones, but start with a '#' sign after the double + // brackets. They also accept a combined form which sets a numeric variable + // to the evaluation of an expression. Both string and numeric variable + // names must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*" to be + // valid, as this helps catch some common errors. if (PatternStr.startswith("[[")) { StringRef UnparsedPatternStr = PatternStr.substr(2); // Find the closing bracket pair ending the match. End is going to be an @@ -424,6 +426,7 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, PatternStr = UnparsedPatternStr.substr(End + 2); bool IsDefinition = false; + bool SubstNeeded = false; // Whether the substitution block is a legacy use of @LINE with string // substitution block syntax. bool IsLegacyLineExpr = false; @@ -454,6 +457,7 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, bool IsPseudo = ParseVarResult->IsPseudo; IsDefinition = (VarEndIdx != StringRef::npos); + SubstNeeded = !IsDefinition; if (IsDefinition) { if ((IsPseudo || !MatchStr.consume_front(":"))) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), @@ -488,22 +492,61 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, if (IsNumBlock) { Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult = parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, - IsLegacyLineExpr, SM); + IsLegacyLineExpr, LineNumber, Context, + SM); if (!ParseResult) { logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } ExpressionAST = std::move(*ParseResult); + SubstNeeded = ExpressionAST != nullptr; if (DefinedNumericVariable) { IsDefinition = true; DefName = (*DefinedNumericVariable)->getName(); - MatchRegexp = StringRef("[0-9]+"); - } else + } + if (SubstNeeded) SubstStr = MatchStr; + else + MatchRegexp = "[0-9]+"; } + // Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]]. + if (IsDefinition) { + RegExStr += '('; + ++SubstInsertIdx; + + if (IsNumBlock) { + FileCheckNumericVariableMatch NumericVariableDefinition = { + *DefinedNumericVariable, CurParen}; + NumericVariableDefs[DefName] = NumericVariableDefinition; + // This store is done here rather than in match() to allow + // parseNumericVariableUse() to get the pointer to the class instance + // of the right variable definition corresponding to a given numeric + // variable use. + Context->GlobalNumericVariableTable[DefName] = + *DefinedNumericVariable; + } else { + VariableDefs[DefName] = CurParen; + // Mark string variable as defined to detect collisions between + // string and numeric variables in parseNumericVariableUse() and + // defineCmdlineVariables() when the latter is created later than the + // former. We cannot reuse GlobalVariableTable for this by populating + // it with an empty string since we would then lose the ability to + // detect the use of an undefined variable in match(). + Context->DefinedVariableTable[DefName] = true; + } + + ++CurParen; + } + + if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM)) + return true; + + if (IsDefinition) + RegExStr += ')'; + // Handle substitutions: [[foo]] and [[#<foo expr>]]. - if (!IsDefinition) { + if (SubstNeeded) { // Handle substitution of string variables that were defined earlier on // the same line by emitting a backreference. Expressions do not // support substituting a numeric variable defined on the same line. @@ -526,37 +569,7 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); } - continue; - } - - // Handle variable definitions: [[<def>:(...)]] and - // [[#(...)<def>:(...)]]. - if (IsNumBlock) { - FileCheckNumericVariableMatch NumericVariableDefinition = { - *DefinedNumericVariable, CurParen}; - NumericVariableDefs[DefName] = NumericVariableDefinition; - // This store is done here rather than in match() to allow - // parseNumericVariableUse() to get the pointer to the class instance - // of the right variable definition corresponding to a given numeric - // variable use. - Context->GlobalNumericVariableTable[DefName] = *DefinedNumericVariable; - } else { - VariableDefs[DefName] = CurParen; - // Mark the string variable as defined to detect collisions between - // string and numeric variables in parseNumericVariableUse() and - // DefineCmdlineVariables() when the latter is created later than the - // former. We cannot reuse GlobalVariableTable for this by populating - // it with an empty string since we would then lose the ability to - // detect the use of an undefined variable in match(). - Context->DefinedVariableTable[DefName] = true; } - RegExStr += '('; - ++CurParen; - - if (AddRegExToRegEx(MatchRegexp, CurParen, SM)) - return true; - - RegExStr += ')'; } // Handle fixed string matches. @@ -607,7 +620,8 @@ Expected<size_t> FileCheckPattern::match(StringRef Buffer, size_t &MatchLen, // If this is a fixed string pattern, just match it now. if (!FixedStr.empty()) { MatchLen = FixedStr.size(); - size_t Pos = Buffer.find(FixedStr); + size_t Pos = IgnoreCase ? Buffer.find_lower(FixedStr) + : Buffer.find(FixedStr); if (Pos == StringRef::npos) return make_error<FileCheckNotFoundError>(); return Pos; @@ -631,10 +645,8 @@ Expected<size_t> FileCheckPattern::match(StringRef Buffer, size_t &MatchLen, for (const auto &Substitution : Substitutions) { // Substitute and check for failure (e.g. use of undefined variable). Expected<std::string> Value = Substitution->getResult(); - if (!Value) { - Context->LineVariable->clearValue(); + if (!Value) return Value.takeError(); - } // Plop it into the regex at the adjusted offset. TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset, @@ -644,11 +656,13 @@ Expected<size_t> FileCheckPattern::match(StringRef Buffer, size_t &MatchLen, // Match the newly constructed regex. RegExToMatch = TmpStr; - Context->LineVariable->clearValue(); } SmallVector<StringRef, 4> MatchInfo; - if (!Regex(RegExToMatch, Regex::Newline).match(Buffer, &MatchInfo)) + unsigned int Flags = Regex::Newline; + if (IgnoreCase) + Flags |= Regex::IgnoreCase; + if (!Regex(RegExToMatch, Flags).match(Buffer, &MatchInfo)) return make_error<FileCheckNotFoundError>(); // Successful regex match. @@ -824,7 +838,7 @@ template <class... Types> FileCheckNumericVariable * FileCheckPatternContext::makeNumericVariable(Types... args) { NumericVariables.push_back( - llvm::make_unique<FileCheckNumericVariable>(args...)); + std::make_unique<FileCheckNumericVariable>(args...)); return NumericVariables.back().get(); } @@ -832,14 +846,14 @@ FileCheckSubstitution * FileCheckPatternContext::makeStringSubstitution(StringRef VarName, size_t InsertIdx) { Substitutions.push_back( - llvm::make_unique<FileCheckStringSubstitution>(this, VarName, InsertIdx)); + std::make_unique<FileCheckStringSubstitution>(this, VarName, InsertIdx)); return Substitutions.back().get(); } FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution( StringRef ExpressionStr, std::unique_ptr<FileCheckExpressionAST> ExpressionAST, size_t InsertIdx) { - Substitutions.push_back(llvm::make_unique<FileCheckNumericSubstitution>( + Substitutions.push_back(std::make_unique<FileCheckNumericSubstitution>( this, ExpressionStr, std::move(ExpressionAST), InsertIdx)); return Substitutions.back().get(); } @@ -1108,16 +1122,22 @@ void FileCheckPatternContext::createLineVariable() { GlobalNumericVariableTable[LineName] = LineVariable; } -bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, - std::vector<FileCheckString> &CheckStrings) { +FileCheck::FileCheck(FileCheckRequest Req) + : Req(Req), PatternContext(std::make_unique<FileCheckPatternContext>()), + CheckStrings(std::make_unique<std::vector<FileCheckString>>()) {} + +FileCheck::~FileCheck() = default; + +bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, + Regex &PrefixRE) { Error DefineError = - PatternContext.defineCmdlineVariables(Req.GlobalDefines, SM); + PatternContext->defineCmdlineVariables(Req.GlobalDefines, SM); if (DefineError) { logAllUnhandledErrors(std::move(DefineError), errs()); return true; } - PatternContext.createLineVariable(); + PatternContext->createLineVariable(); std::vector<FileCheckPattern> ImplicitNegativeChecks; for (const auto &PatternString : Req.ImplicitCheckNot) { @@ -1133,7 +1153,7 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); ImplicitNegativeChecks.push_back( - FileCheckPattern(Check::CheckNot, &PatternContext)); + FileCheckPattern(Check::CheckNot, PatternContext.get())); ImplicitNegativeChecks.back().parsePattern(PatternInBuffer, "IMPLICIT-CHECK", SM, Req); } @@ -1196,7 +1216,7 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); // Parse the pattern. - FileCheckPattern P(CheckTy, &PatternContext, LineNumber); + FileCheckPattern P(CheckTy, PatternContext.get(), LineNumber); if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req)) return true; @@ -1214,7 +1234,7 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, // Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them. if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame || CheckTy == Check::CheckEmpty) && - CheckStrings.empty()) { + CheckStrings->empty()) { StringRef Type = CheckTy == Check::CheckNext ? "NEXT" : CheckTy == Check::CheckEmpty ? "EMPTY" : "SAME"; @@ -1232,21 +1252,21 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, } // Okay, add the string we captured to the output vector and move on. - CheckStrings.emplace_back(P, UsedPrefix, PatternLoc); - std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); + CheckStrings->emplace_back(P, UsedPrefix, PatternLoc); + std::swap(DagNotMatches, CheckStrings->back().DagNotStrings); DagNotMatches = ImplicitNegativeChecks; } // 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, &PatternContext, LineNumber + 1), + CheckStrings->emplace_back( + FileCheckPattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1), *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data())); - std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); + std::swap(DagNotMatches, CheckStrings->back().DagNotStrings); } - if (CheckStrings.empty()) { + if (CheckStrings->empty()) { errs() << "error: no check strings found with prefix" << (Req.CheckPrefixes.size() > 1 ? "es " : " "); auto I = Req.CheckPrefixes.begin(); @@ -1704,7 +1724,7 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, // A check prefix must contain only alphanumeric, hyphens and underscores. static bool ValidateCheckPrefix(StringRef CheckPrefix) { - Regex Validator("^[a-zA-Z0-9_-]*$"); + static const Regex Validator("^[a-zA-Z0-9_-]*$"); return Validator.match(CheckPrefix); } @@ -1759,11 +1779,32 @@ Error FileCheckPatternContext::defineCmdlineVariables( unsigned I = 0; Error Errs = Error::success(); std::string CmdlineDefsDiag; - StringRef Prefix1 = "Global define #"; - StringRef Prefix2 = ": "; - for (StringRef CmdlineDef : CmdlineDefines) - CmdlineDefsDiag += - (Prefix1 + Twine(++I) + Prefix2 + CmdlineDef + "\n").str(); + SmallVector<std::pair<size_t, size_t>, 4> CmdlineDefsIndices; + for (StringRef CmdlineDef : CmdlineDefines) { + std::string DefPrefix = ("Global define #" + Twine(++I) + ": ").str(); + size_t EqIdx = CmdlineDef.find('='); + if (EqIdx == StringRef::npos) { + CmdlineDefsIndices.push_back(std::make_pair(CmdlineDefsDiag.size(), 0)); + continue; + } + // Numeric variable definition. + if (CmdlineDef[0] == '#') { + // Append a copy of the command-line definition adapted to use the same + // format as in the input file to be able to reuse + // parseNumericSubstitutionBlock. + CmdlineDefsDiag += (DefPrefix + CmdlineDef + " (parsed as: [[").str(); + std::string SubstitutionStr = CmdlineDef; + SubstitutionStr[EqIdx] = ':'; + CmdlineDefsIndices.push_back( + std::make_pair(CmdlineDefsDiag.size(), SubstitutionStr.size())); + CmdlineDefsDiag += (SubstitutionStr + Twine("]])\n")).str(); + } else { + CmdlineDefsDiag += DefPrefix; + CmdlineDefsIndices.push_back( + std::make_pair(CmdlineDefsDiag.size(), CmdlineDef.size())); + CmdlineDefsDiag += (CmdlineDef + "\n").str(); + } + } // Create a buffer with fake command line content in order to display // parsing diagnostic with location information and point to the @@ -1773,14 +1814,10 @@ Error FileCheckPatternContext::defineCmdlineVariables( StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer(); SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc()); - SmallVector<StringRef, 4> CmdlineDefsDiagVec; - CmdlineDefsDiagRef.split(CmdlineDefsDiagVec, '\n', -1 /*MaxSplit*/, - false /*KeepEmpty*/); - for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) { - unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size(); - StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart); - size_t EqIdx = CmdlineDef.find('='); - if (EqIdx == StringRef::npos) { + for (std::pair<size_t, size_t> CmdlineDefIndices : CmdlineDefsIndices) { + StringRef CmdlineDef = CmdlineDefsDiagRef.substr(CmdlineDefIndices.first, + CmdlineDefIndices.second); + if (CmdlineDef.empty()) { Errs = joinErrors( std::move(Errs), FileCheckErrorDiagnostic::get( @@ -1790,31 +1827,35 @@ Error FileCheckPatternContext::defineCmdlineVariables( // Numeric variable definition. if (CmdlineDef[0] == '#') { - StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1); - Expected<FileCheckNumericVariable *> ParseResult = - FileCheckPattern::parseNumericVariableDefinition(CmdlineName, this, - None, SM); - if (!ParseResult) { - Errs = joinErrors(std::move(Errs), ParseResult.takeError()); + // Now parse the definition both to check that the syntax is correct and + // to create the necessary class instance. + StringRef CmdlineDefExpr = CmdlineDef.substr(1); + Optional<FileCheckNumericVariable *> DefinedNumericVariable; + Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionASTResult = + FileCheckPattern::parseNumericSubstitutionBlock( + CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM); + if (!ExpressionASTResult) { + Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError()); continue; } - - StringRef CmdlineVal = CmdlineDef.substr(EqIdx + 1); - uint64_t Val; - if (CmdlineVal.getAsInteger(10, Val)) { - Errs = joinErrors(std::move(Errs), - FileCheckErrorDiagnostic::get( - SM, CmdlineVal, - "invalid value in numeric variable definition '" + - CmdlineVal + "'")); + std::unique_ptr<FileCheckExpressionAST> ExpressionAST = + std::move(*ExpressionASTResult); + // Now evaluate the expression whose value this variable should be set + // to, since the expression of a command-line variable definition should + // only use variables defined earlier on the command-line. If not, this + // is an error and we report it. + Expected<uint64_t> Value = ExpressionAST->eval(); + if (!Value) { + Errs = joinErrors(std::move(Errs), Value.takeError()); continue; } - FileCheckNumericVariable *DefinedNumericVariable = *ParseResult; - DefinedNumericVariable->setValue(Val); + + assert(DefinedNumericVariable && "No variable defined"); + (*DefinedNumericVariable)->setValue(*Value); // Record this variable definition. - GlobalNumericVariableTable[DefinedNumericVariable->getName()] = - DefinedNumericVariable; + GlobalNumericVariableTable[(*DefinedNumericVariable)->getName()] = + *DefinedNumericVariable; } else { // String variable definition. std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('='); @@ -1851,7 +1892,7 @@ Error FileCheckPatternContext::defineCmdlineVariables( } GlobalVariableTable.insert(CmdlineNameVal); // Mark the string variable as defined to detect collisions between - // string and numeric variables in DefineCmdlineVariables when the latter + // string and numeric variables in defineCmdlineVariables when the latter // is created later than the former. We cannot reuse GlobalVariableTable // for this by populating it with an empty string since we would then // lose the ability to detect the use of an undefined variable in @@ -1887,18 +1928,17 @@ void FileCheckPatternContext::clearLocalVars() { GlobalNumericVariableTable.erase(Var); } -bool FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer, - ArrayRef<FileCheckString> CheckStrings, +bool FileCheck::checkInput(SourceMgr &SM, StringRef Buffer, std::vector<FileCheckDiag> *Diags) { bool ChecksFailed = false; - unsigned i = 0, j = 0, e = CheckStrings.size(); + unsigned i = 0, j = 0, e = CheckStrings->size(); while (true) { StringRef CheckRegion; if (j == e) { CheckRegion = Buffer; } else { - const FileCheckString &CheckLabelStr = CheckStrings[j]; + const FileCheckString &CheckLabelStr = (*CheckStrings)[j]; if (CheckLabelStr.Pat.getCheckTy() != Check::CheckLabel) { ++j; continue; @@ -1921,10 +1961,10 @@ bool FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer, // CHECK-LABEL and it would clear variables defined on the command-line // before they get used. if (i != 0 && Req.EnableVarScope) - PatternContext.clearLocalVars(); + PatternContext->clearLocalVars(); for (; i != j; ++i) { - const FileCheckString &CheckStr = CheckStrings[i]; + const FileCheckString &CheckStr = (*CheckStrings)[i]; // Check each string within the scanned region, including a second check // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG) |