aboutsummaryrefslogtreecommitdiff
path: root/lib/Support/FileCheck.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Support/FileCheck.cpp')
-rw-r--r--lib/Support/FileCheck.cpp356
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)