aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Support/FileCheck.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-07-26 19:36:28 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-07-26 19:36:28 +0000
commitcfca06d7963fa0909f90483b42a6d7d194d01e08 (patch)
tree209fb2a2d68f8f277793fc8df46c753d31bc853b /llvm/lib/Support/FileCheck.cpp
parent706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff)
downloadsrc-cfca06d7963fa0909f90483b42a6d7d194d01e08.tar.gz
src-cfca06d7963fa0909f90483b42a6d7d194d01e08.zip
Vendor import of llvm-project master 2e10b7a39b9, the last commit beforevendor/llvm-project/llvmorg-11-init-20887-g2e10b7a39b9vendor/llvm-project/master
the llvmorg-12-init tag, from which release/11.x was branched.
Notes
Notes: svn path=/vendor/llvm-project/master/; revision=363578 svn path=/vendor/llvm-project/llvmorg-11-init-20887-g2e10b7a39b9/; revision=363579; tag=vendor/llvm-project/llvmorg-11-init-20887-g2e10b7a39b9
Diffstat (limited to 'llvm/lib/Support/FileCheck.cpp')
-rw-r--r--llvm/lib/Support/FileCheck.cpp928
1 files changed, 767 insertions, 161 deletions
diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp
index 2261ecc236c2..d0e79c675bcb 100644
--- a/llvm/lib/Support/FileCheck.cpp
+++ b/llvm/lib/Support/FileCheck.cpp
@@ -17,6 +17,7 @@
#include "FileCheckImpl.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Support/CheckedArithmetic.h"
#include "llvm/Support/FormatVariadic.h"
#include <cstdint>
#include <list>
@@ -25,17 +26,301 @@
using namespace llvm;
-Expected<uint64_t> NumericVariableUse::eval() const {
- Optional<uint64_t> Value = Variable->getValue();
+StringRef ExpressionFormat::toString() const {
+ switch (Value) {
+ case Kind::NoFormat:
+ return StringRef("<none>");
+ case Kind::Unsigned:
+ return StringRef("%u");
+ case Kind::Signed:
+ return StringRef("%d");
+ case Kind::HexUpper:
+ return StringRef("%X");
+ case Kind::HexLower:
+ return StringRef("%x");
+ }
+ llvm_unreachable("unknown expression format");
+}
+
+Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
+ switch (Value) {
+ case Kind::Unsigned:
+ return StringRef("[0-9]+");
+ case Kind::Signed:
+ return StringRef("-?[0-9]+");
+ case Kind::HexUpper:
+ return StringRef("[0-9A-F]+");
+ case Kind::HexLower:
+ return StringRef("[0-9a-f]+");
+ default:
+ return createStringError(std::errc::invalid_argument,
+ "trying to match value with invalid format");
+ }
+}
+
+Expected<std::string>
+ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const {
+ if (Value == Kind::Signed) {
+ Expected<int64_t> SignedValue = IntegerValue.getSignedValue();
+ if (!SignedValue)
+ return SignedValue.takeError();
+ return itostr(*SignedValue);
+ }
+
+ Expected<uint64_t> UnsignedValue = IntegerValue.getUnsignedValue();
+ if (!UnsignedValue)
+ return UnsignedValue.takeError();
+ switch (Value) {
+ case Kind::Unsigned:
+ return utostr(*UnsignedValue);
+ case Kind::HexUpper:
+ return utohexstr(*UnsignedValue, /*LowerCase=*/false);
+ case Kind::HexLower:
+ return utohexstr(*UnsignedValue, /*LowerCase=*/true);
+ default:
+ return createStringError(std::errc::invalid_argument,
+ "trying to match value with invalid format");
+ }
+}
+
+Expected<ExpressionValue>
+ExpressionFormat::valueFromStringRepr(StringRef StrVal,
+ const SourceMgr &SM) const {
+ bool ValueIsSigned = Value == Kind::Signed;
+ StringRef OverflowErrorStr = "unable to represent numeric value";
+ if (ValueIsSigned) {
+ int64_t SignedValue;
+
+ if (StrVal.getAsInteger(10, SignedValue))
+ return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr);
+
+ return ExpressionValue(SignedValue);
+ }
+
+ bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
+ uint64_t UnsignedValue;
+ if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue))
+ return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr);
+
+ return ExpressionValue(UnsignedValue);
+}
+
+static int64_t getAsSigned(uint64_t UnsignedValue) {
+ // Use memcpy to reinterpret the bitpattern in Value since casting to
+ // signed is implementation-defined if the unsigned value is too big to be
+ // represented in the signed type and using an union violates type aliasing
+ // rules.
+ int64_t SignedValue;
+ memcpy(&SignedValue, &UnsignedValue, sizeof(SignedValue));
+ return SignedValue;
+}
+
+Expected<int64_t> ExpressionValue::getSignedValue() const {
+ if (Negative)
+ return getAsSigned(Value);
+
+ if (Value > (uint64_t)std::numeric_limits<int64_t>::max())
+ return make_error<OverflowError>();
+
+ // Value is in the representable range of int64_t so we can use cast.
+ return static_cast<int64_t>(Value);
+}
+
+Expected<uint64_t> ExpressionValue::getUnsignedValue() const {
+ if (Negative)
+ return make_error<OverflowError>();
+
+ return Value;
+}
+
+ExpressionValue ExpressionValue::getAbsolute() const {
+ if (!Negative)
+ return *this;
+
+ int64_t SignedValue = getAsSigned(Value);
+ int64_t MaxInt64 = std::numeric_limits<int64_t>::max();
+ // Absolute value can be represented as int64_t.
+ if (SignedValue >= -MaxInt64)
+ return ExpressionValue(-getAsSigned(Value));
+
+ // -X == -(max int64_t + Rem), negate each component independently.
+ SignedValue += MaxInt64;
+ uint64_t RemainingValueAbsolute = -SignedValue;
+ return ExpressionValue(MaxInt64 + RemainingValueAbsolute);
+}
+
+Expected<ExpressionValue> llvm::operator+(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ if (LeftOperand.isNegative() && RightOperand.isNegative()) {
+ int64_t LeftValue = cantFail(LeftOperand.getSignedValue());
+ int64_t RightValue = cantFail(RightOperand.getSignedValue());
+ Optional<int64_t> Result = checkedAdd<int64_t>(LeftValue, RightValue);
+ if (!Result)
+ return make_error<OverflowError>();
+
+ return ExpressionValue(*Result);
+ }
+
+ // (-A) + B == B - A.
+ if (LeftOperand.isNegative())
+ return RightOperand - LeftOperand.getAbsolute();
+
+ // A + (-B) == A - B.
+ if (RightOperand.isNegative())
+ return LeftOperand - RightOperand.getAbsolute();
+
+ // Both values are positive at this point.
+ uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ Optional<uint64_t> Result =
+ checkedAddUnsigned<uint64_t>(LeftValue, RightValue);
+ if (!Result)
+ return make_error<OverflowError>();
+
+ return ExpressionValue(*Result);
+}
+
+Expected<ExpressionValue> llvm::operator-(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ // Result will be negative and thus might underflow.
+ if (LeftOperand.isNegative() && !RightOperand.isNegative()) {
+ int64_t LeftValue = cantFail(LeftOperand.getSignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ // Result <= -1 - (max int64_t) which overflows on 1- and 2-complement.
+ if (RightValue > (uint64_t)std::numeric_limits<int64_t>::max())
+ return make_error<OverflowError>();
+ Optional<int64_t> Result =
+ checkedSub(LeftValue, static_cast<int64_t>(RightValue));
+ if (!Result)
+ return make_error<OverflowError>();
+
+ return ExpressionValue(*Result);
+ }
+
+ // (-A) - (-B) == B - A.
+ if (LeftOperand.isNegative())
+ return RightOperand.getAbsolute() - LeftOperand.getAbsolute();
+
+ // A - (-B) == A + B.
+ if (RightOperand.isNegative())
+ return LeftOperand + RightOperand.getAbsolute();
+
+ // Both values are positive at this point.
+ uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ if (LeftValue >= RightValue)
+ return ExpressionValue(LeftValue - RightValue);
+ else {
+ uint64_t AbsoluteDifference = RightValue - LeftValue;
+ uint64_t MaxInt64 = std::numeric_limits<int64_t>::max();
+ // Value might underflow.
+ if (AbsoluteDifference > MaxInt64) {
+ AbsoluteDifference -= MaxInt64;
+ int64_t Result = -MaxInt64;
+ int64_t MinInt64 = std::numeric_limits<int64_t>::min();
+ // Underflow, tested by:
+ // abs(Result + (max int64_t)) > abs((min int64_t) + (max int64_t))
+ if (AbsoluteDifference > static_cast<uint64_t>(-(MinInt64 - Result)))
+ return make_error<OverflowError>();
+ Result -= static_cast<int64_t>(AbsoluteDifference);
+ return ExpressionValue(Result);
+ }
+
+ return ExpressionValue(-static_cast<int64_t>(AbsoluteDifference));
+ }
+}
+
+Expected<ExpressionValue> llvm::operator*(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ // -A * -B == A * B
+ if (LeftOperand.isNegative() && RightOperand.isNegative())
+ return LeftOperand.getAbsolute() * RightOperand.getAbsolute();
+
+ // A * -B == -B * A
+ if (RightOperand.isNegative())
+ return RightOperand * LeftOperand;
+
+ assert(!RightOperand.isNegative() && "Unexpected negative operand!");
+
+ // Result will be negative and can underflow.
+ if (LeftOperand.isNegative()) {
+ auto Result = LeftOperand.getAbsolute() * RightOperand.getAbsolute();
+ if (!Result)
+ return Result;
+
+ return ExpressionValue(0) - *Result;
+ }
+
+ // Result will be positive and can overflow.
+ uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ Optional<uint64_t> Result =
+ checkedMulUnsigned<uint64_t>(LeftValue, RightValue);
+ if (!Result)
+ return make_error<OverflowError>();
+
+ return ExpressionValue(*Result);
+}
+
+Expected<ExpressionValue> llvm::operator/(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ // -A / -B == A / B
+ if (LeftOperand.isNegative() && RightOperand.isNegative())
+ return LeftOperand.getAbsolute() / RightOperand.getAbsolute();
+
+ // Check for divide by zero.
+ if (RightOperand == ExpressionValue(0))
+ return make_error<OverflowError>();
+
+ // Result will be negative and can underflow.
+ if (LeftOperand.isNegative() || RightOperand.isNegative())
+ return ExpressionValue(0) -
+ cantFail(LeftOperand.getAbsolute() / RightOperand.getAbsolute());
+
+ uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ return ExpressionValue(LeftValue / RightValue);
+}
+
+Expected<ExpressionValue> llvm::max(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ if (LeftOperand.isNegative() && RightOperand.isNegative()) {
+ int64_t LeftValue = cantFail(LeftOperand.getSignedValue());
+ int64_t RightValue = cantFail(RightOperand.getSignedValue());
+ return ExpressionValue(std::max(LeftValue, RightValue));
+ }
+
+ if (!LeftOperand.isNegative() && !RightOperand.isNegative()) {
+ uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ return ExpressionValue(std::max(LeftValue, RightValue));
+ }
+
+ if (LeftOperand.isNegative())
+ return RightOperand;
+
+ return LeftOperand;
+}
+
+Expected<ExpressionValue> llvm::min(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ if (cantFail(max(LeftOperand, RightOperand)) == LeftOperand)
+ return RightOperand;
+
+ return LeftOperand;
+}
+
+Expected<ExpressionValue> NumericVariableUse::eval() const {
+ Optional<ExpressionValue> Value = Variable->getValue();
if (Value)
return *Value;
- return make_error<UndefVarError>(Name);
+ return make_error<UndefVarError>(getExpressionStr());
}
-Expected<uint64_t> BinaryOperation::eval() const {
- Expected<uint64_t> LeftOp = LeftOperand->eval();
- Expected<uint64_t> RightOp = RightOperand->eval();
+Expected<ExpressionValue> BinaryOperation::eval() const {
+ Expected<ExpressionValue> LeftOp = LeftOperand->eval();
+ Expected<ExpressionValue> RightOp = RightOperand->eval();
// Bubble up any error (e.g. undefined variables) in the recursive
// evaluation.
@@ -51,11 +336,42 @@ Expected<uint64_t> BinaryOperation::eval() const {
return EvalBinop(*LeftOp, *RightOp);
}
+Expected<ExpressionFormat>
+BinaryOperation::getImplicitFormat(const SourceMgr &SM) const {
+ Expected<ExpressionFormat> LeftFormat = LeftOperand->getImplicitFormat(SM);
+ Expected<ExpressionFormat> RightFormat = RightOperand->getImplicitFormat(SM);
+ if (!LeftFormat || !RightFormat) {
+ Error Err = Error::success();
+ if (!LeftFormat)
+ Err = joinErrors(std::move(Err), LeftFormat.takeError());
+ if (!RightFormat)
+ Err = joinErrors(std::move(Err), RightFormat.takeError());
+ return std::move(Err);
+ }
+
+ if (*LeftFormat != ExpressionFormat::Kind::NoFormat &&
+ *RightFormat != ExpressionFormat::Kind::NoFormat &&
+ *LeftFormat != *RightFormat)
+ return ErrorDiagnostic::get(
+ SM, getExpressionStr(),
+ "implicit format conflict between '" + LeftOperand->getExpressionStr() +
+ "' (" + LeftFormat->toString() + ") and '" +
+ RightOperand->getExpressionStr() + "' (" + RightFormat->toString() +
+ "), need an explicit format specifier");
+
+ return *LeftFormat != ExpressionFormat::Kind::NoFormat ? *LeftFormat
+ : *RightFormat;
+}
+
Expected<std::string> NumericSubstitution::getResult() const {
- Expected<uint64_t> EvaluatedValue = ExpressionASTPointer->eval();
+ assert(ExpressionPointer->getAST() != nullptr &&
+ "Substituting empty expression");
+ Expected<ExpressionValue> EvaluatedValue =
+ ExpressionPointer->getAST()->eval();
if (!EvaluatedValue)
return EvaluatedValue.takeError();
- return utostr(*EvaluatedValue);
+ ExpressionFormat Format = ExpressionPointer->getFormat();
+ return Format.getMatchingString(*EvaluatedValue);
}
Expected<std::string> StringSubstitution::getResult() const {
@@ -66,30 +382,27 @@ Expected<std::string> StringSubstitution::getResult() const {
return Regex::escape(*VarVal);
}
-bool Pattern::isValidVarNameStart(char C) { return C == '_' || isalpha(C); }
+bool Pattern::isValidVarNameStart(char C) { return C == '_' || isAlpha(C); }
Expected<Pattern::VariableProperties>
Pattern::parseVariable(StringRef &Str, const SourceMgr &SM) {
if (Str.empty())
return ErrorDiagnostic::get(SM, Str, "empty variable name");
- bool ParsedOneChar = false;
- unsigned I = 0;
+ size_t I = 0;
bool IsPseudo = Str[0] == '@';
// Global vars start with '$'.
if (Str[0] == '$' || IsPseudo)
++I;
- for (unsigned E = Str.size(); I != E; ++I) {
- if (!ParsedOneChar && !isValidVarNameStart(Str[I]))
- return ErrorDiagnostic::get(SM, Str, "invalid variable name");
+ if (!isValidVarNameStart(Str[I++]))
+ return ErrorDiagnostic::get(SM, Str, "invalid variable name");
+ for (size_t E = Str.size(); I != E; ++I)
// Variable names are composed of alphanumeric characters and underscores.
- if (Str[I] != '_' && !isalnum(Str[I]))
+ if (Str[I] != '_' && !isAlnum(Str[I]))
break;
- ParsedOneChar = true;
- }
StringRef Name = Str.take_front(I);
Str = Str.substr(I);
@@ -107,13 +420,15 @@ static char popFront(StringRef &S) {
return C;
}
+char OverflowError::ID = 0;
char UndefVarError::ID = 0;
char ErrorDiagnostic::ID = 0;
char NotFoundError::ID = 0;
Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context,
- Optional<size_t> LineNumber, const SourceMgr &SM) {
+ Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
+ const SourceMgr &SM) {
Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
if (!ParseVarResult)
return ParseVarResult.takeError();
@@ -137,10 +452,14 @@ Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
NumericVariable *DefinedNumericVariable;
auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
- if (VarTableIter != Context->GlobalNumericVariableTable.end())
+ if (VarTableIter != Context->GlobalNumericVariableTable.end()) {
DefinedNumericVariable = VarTableIter->second;
- else
- DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber);
+ if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat)
+ return ErrorDiagnostic::get(
+ SM, Expr, "format different from previous variable definition");
+ } else
+ DefinedNumericVariable =
+ Context->makeNumericVariable(Name, ImplicitFormat, LineNumber);
return DefinedNumericVariable;
}
@@ -165,7 +484,8 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
if (VarTableIter != Context->GlobalNumericVariableTable.end())
NumericVariable = VarTableIter->second;
else {
- NumericVariable = Context->makeNumericVariable(Name);
+ NumericVariable = Context->makeNumericVariable(
+ Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
Context->GlobalNumericVariableTable[Name] = NumericVariable;
}
@@ -180,16 +500,36 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(
}
Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
- StringRef &Expr, AllowedOperand AO, Optional<size_t> LineNumber,
- FileCheckPatternContext *Context, const SourceMgr &SM) {
+ StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint,
+ Optional<size_t> LineNumber, FileCheckPatternContext *Context,
+ const SourceMgr &SM) {
+ if (Expr.startswith("(")) {
+ if (AO != AllowedOperand::Any)
+ return ErrorDiagnostic::get(
+ SM, Expr, "parenthesized expression not permitted here");
+ return parseParenExpr(Expr, LineNumber, Context, SM);
+ }
+
if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {
// Try to parse as a numeric variable use.
Expected<Pattern::VariableProperties> ParseVarResult =
parseVariable(Expr, SM);
- if (ParseVarResult)
+ if (ParseVarResult) {
+ // Try to parse a function call.
+ if (Expr.ltrim(SpaceChars).startswith("(")) {
+ if (AO != AllowedOperand::Any)
+ return ErrorDiagnostic::get(SM, ParseVarResult->Name,
+ "unexpected function call");
+
+ return parseCallExpr(Expr, ParseVarResult->Name, LineNumber, Context,
+ SM);
+ }
+
return parseNumericVariableUse(ParseVarResult->Name,
ParseVarResult->IsPseudo, LineNumber,
Context, SM);
+ }
+
if (AO == AllowedOperand::LineVar)
return ParseVarResult.takeError();
// Ignore the error and retry parsing as a literal.
@@ -197,41 +537,79 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
}
// Otherwise, parse it as a literal.
- uint64_t LiteralValue;
- if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue))
- return std::make_unique<ExpressionLiteral>(LiteralValue);
-
- return ErrorDiagnostic::get(SM, Expr,
- "invalid operand format '" + Expr + "'");
+ int64_t SignedLiteralValue;
+ uint64_t UnsignedLiteralValue;
+ StringRef SaveExpr = Expr;
+ // Accept both signed and unsigned literal, default to signed literal.
+ if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
+ UnsignedLiteralValue))
+ return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()),
+ UnsignedLiteralValue);
+ Expr = SaveExpr;
+ if (AO == AllowedOperand::Any && !Expr.consumeInteger(0, SignedLiteralValue))
+ return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()),
+ SignedLiteralValue);
+
+ return ErrorDiagnostic::get(
+ SM, Expr,
+ Twine("invalid ") +
+ (MaybeInvalidConstraint ? "matching constraint or " : "") +
+ "operand format");
}
-static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
- return LeftOp + RightOp;
-}
+Expected<std::unique_ptr<ExpressionAST>>
+Pattern::parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber,
+ FileCheckPatternContext *Context, const SourceMgr &SM) {
+ Expr = Expr.ltrim(SpaceChars);
+ assert(Expr.startswith("("));
+
+ // Parse right operand.
+ Expr.consume_front("(");
+ Expr = Expr.ltrim(SpaceChars);
+ if (Expr.empty())
+ return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
-static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
- return LeftOp - RightOp;
+ // Note: parseNumericOperand handles nested opening parentheses.
+ Expected<std::unique_ptr<ExpressionAST>> SubExprResult = parseNumericOperand(
+ Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
+ Context, SM);
+ Expr = Expr.ltrim(SpaceChars);
+ while (SubExprResult && !Expr.empty() && !Expr.startswith(")")) {
+ StringRef OrigExpr = Expr;
+ SubExprResult = parseBinop(OrigExpr, Expr, std::move(*SubExprResult), false,
+ LineNumber, Context, SM);
+ Expr = Expr.ltrim(SpaceChars);
+ }
+ if (!SubExprResult)
+ return SubExprResult;
+
+ if (!Expr.consume_front(")")) {
+ return ErrorDiagnostic::get(SM, Expr,
+ "missing ')' at end of nested expression");
+ }
+ return SubExprResult;
}
Expected<std::unique_ptr<ExpressionAST>>
-Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
+Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
+ std::unique_ptr<ExpressionAST> LeftOp,
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM) {
- Expr = Expr.ltrim(SpaceChars);
- if (Expr.empty())
+ RemainingExpr = RemainingExpr.ltrim(SpaceChars);
+ if (RemainingExpr.empty())
return std::move(LeftOp);
// Check if this is a supported operation and select a function to perform
// it.
- SMLoc OpLoc = SMLoc::getFromPointer(Expr.data());
- char Operator = popFront(Expr);
+ SMLoc OpLoc = SMLoc::getFromPointer(RemainingExpr.data());
+ char Operator = popFront(RemainingExpr);
binop_eval_t EvalBinop;
switch (Operator) {
case '+':
- EvalBinop = add;
+ EvalBinop = operator+;
break;
case '-':
- EvalBinop = sub;
+ EvalBinop = operator-;
break;
default:
return ErrorDiagnostic::get(
@@ -239,29 +617,145 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
}
// Parse right operand.
- Expr = Expr.ltrim(SpaceChars);
- if (Expr.empty())
- return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
+ RemainingExpr = RemainingExpr.ltrim(SpaceChars);
+ if (RemainingExpr.empty())
+ return ErrorDiagnostic::get(SM, RemainingExpr,
+ "missing operand in expression");
// The second operand in a legacy @LINE expression is always a literal.
AllowedOperand AO =
- IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any;
+ IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
- parseNumericOperand(Expr, AO, LineNumber, Context, SM);
+ parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false,
+ LineNumber, Context, SM);
if (!RightOpResult)
return RightOpResult;
- Expr = Expr.ltrim(SpaceChars);
- return std::make_unique<BinaryOperation>(EvalBinop, std::move(LeftOp),
+ Expr = Expr.drop_back(RemainingExpr.size());
+ return std::make_unique<BinaryOperation>(Expr, EvalBinop, std::move(LeftOp),
std::move(*RightOpResult));
}
-Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
+Expected<std::unique_ptr<ExpressionAST>>
+Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName,
+ Optional<size_t> LineNumber,
+ FileCheckPatternContext *Context, const SourceMgr &SM) {
+ Expr = Expr.ltrim(SpaceChars);
+ assert(Expr.startswith("("));
+
+ auto OptFunc = StringSwitch<Optional<binop_eval_t>>(FuncName)
+ .Case("add", operator+)
+ .Case("div", operator/)
+ .Case("max", max)
+ .Case("min", min)
+ .Case("mul", operator*)
+ .Case("sub", operator-)
+ .Default(None);
+
+ if (!OptFunc)
+ return ErrorDiagnostic::get(
+ SM, FuncName, Twine("call to undefined function '") + FuncName + "'");
+
+ Expr.consume_front("(");
+ Expr = Expr.ltrim(SpaceChars);
+
+ // Parse call arguments, which are comma separated.
+ SmallVector<std::unique_ptr<ExpressionAST>, 4> Args;
+ while (!Expr.empty() && !Expr.startswith(")")) {
+ if (Expr.startswith(","))
+ return ErrorDiagnostic::get(SM, Expr, "missing argument");
+
+ // Parse the argument, which is an arbitary expression.
+ StringRef OuterBinOpExpr = Expr;
+ Expected<std::unique_ptr<ExpressionAST>> Arg = parseNumericOperand(
+ Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber,
+ Context, SM);
+ while (Arg && !Expr.empty()) {
+ Expr = Expr.ltrim(SpaceChars);
+ // Have we reached an argument terminator?
+ if (Expr.startswith(",") || Expr.startswith(")"))
+ break;
+
+ // Arg = Arg <op> <expr>
+ Arg = parseBinop(OuterBinOpExpr, Expr, std::move(*Arg), false, LineNumber,
+ Context, SM);
+ }
+
+ // Prefer an expression error over a generic invalid argument message.
+ if (!Arg)
+ return Arg.takeError();
+ Args.push_back(std::move(*Arg));
+
+ // Have we parsed all available arguments?
+ Expr = Expr.ltrim(SpaceChars);
+ if (!Expr.consume_front(","))
+ break;
+
+ Expr = Expr.ltrim(SpaceChars);
+ if (Expr.startswith(")"))
+ return ErrorDiagnostic::get(SM, Expr, "missing argument");
+ }
+
+ if (!Expr.consume_front(")"))
+ return ErrorDiagnostic::get(SM, Expr,
+ "missing ')' at end of call expression");
+
+ const unsigned NumArgs = Args.size();
+ if (NumArgs == 2)
+ return std::make_unique<BinaryOperation>(Expr, *OptFunc, std::move(Args[0]),
+ std::move(Args[1]));
+
+ // TODO: Support more than binop_eval_t.
+ return ErrorDiagnostic::get(SM, FuncName,
+ Twine("function '") + FuncName +
+ Twine("' takes 2 arguments but ") +
+ Twine(NumArgs) + " given");
+}
+
+Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM) {
std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr;
StringRef DefExpr = StringRef();
DefinedNumericVariable = None;
+ ExpressionFormat ExplicitFormat = ExpressionFormat();
+
+ // Parse format specifier (NOTE: ',' is also an argument seperator).
+ size_t FormatSpecEnd = Expr.find(',');
+ size_t FunctionStart = Expr.find('(');
+ if (FormatSpecEnd != StringRef::npos && FormatSpecEnd < FunctionStart) {
+ Expr = Expr.ltrim(SpaceChars);
+ if (!Expr.consume_front("%"))
+ return ErrorDiagnostic::get(
+ SM, Expr, "invalid matching format specification in expression");
+
+ // Check for unknown matching format specifier and set matching format in
+ // class instance representing this expression.
+ SMLoc fmtloc = SMLoc::getFromPointer(Expr.data());
+ switch (popFront(Expr)) {
+ case 'u':
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
+ break;
+ case 'd':
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Signed);
+ break;
+ case 'x':
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower);
+ break;
+ case 'X':
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper);
+ break;
+ default:
+ return ErrorDiagnostic::get(SM, fmtloc,
+ "invalid format specifier in expression");
+ }
+
+ Expr = Expr.ltrim(SpaceChars);
+ if (!Expr.consume_front(","))
+ return ErrorDiagnostic::get(
+ SM, Expr, "invalid matching format specification in expression");
+ }
+
// Save variable definition expression if any.
size_t DefEnd = Expr.find(':');
if (DefEnd != StringRef::npos) {
@@ -269,18 +763,30 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
Expr = Expr.substr(DefEnd + 1);
}
+ // Parse matching constraint.
+ Expr = Expr.ltrim(SpaceChars);
+ bool HasParsedValidConstraint = false;
+ if (Expr.consume_front("=="))
+ HasParsedValidConstraint = true;
+
// Parse the expression itself.
Expr = Expr.ltrim(SpaceChars);
- if (!Expr.empty()) {
+ if (Expr.empty()) {
+ if (HasParsedValidConstraint)
+ return ErrorDiagnostic::get(
+ SM, Expr, "empty numeric expression should not have a constraint");
+ } else {
+ Expr = Expr.rtrim(SpaceChars);
+ StringRef OuterBinOpExpr = Expr;
// 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<ExpressionAST>> ParseResult =
- parseNumericOperand(Expr, AO, LineNumber, Context, SM);
+ Expected<std::unique_ptr<ExpressionAST>> ParseResult = parseNumericOperand(
+ Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM);
while (ParseResult && !Expr.empty()) {
- ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr,
- LineNumber, Context, SM);
+ ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult),
+ IsLegacyLineExpr, LineNumber, Context, SM);
// Legacy @LINE expressions only allow 2 operands.
if (ParseResult && IsLegacyLineExpr && !Expr.empty())
return ErrorDiagnostic::get(
@@ -288,22 +794,42 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
"unexpected characters at end of expression '" + Expr + "'");
}
if (!ParseResult)
- return ParseResult;
+ return ParseResult.takeError();
ExpressionASTPointer = std::move(*ParseResult);
}
+ // Select format of the expression, i.e. (i) its explicit format, if any,
+ // otherwise (ii) its implicit format, if any, otherwise (iii) the default
+ // format (unsigned). Error out in case of conflicting implicit format
+ // without explicit format.
+ ExpressionFormat Format;
+ if (ExplicitFormat)
+ Format = ExplicitFormat;
+ else if (ExpressionASTPointer) {
+ Expected<ExpressionFormat> ImplicitFormat =
+ ExpressionASTPointer->getImplicitFormat(SM);
+ if (!ImplicitFormat)
+ return ImplicitFormat.takeError();
+ Format = *ImplicitFormat;
+ }
+ if (!Format)
+ Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
+
+ std::unique_ptr<Expression> ExpressionPointer =
+ std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);
+
// Parse the numeric variable definition.
if (DefEnd != StringRef::npos) {
DefExpr = DefExpr.ltrim(SpaceChars);
- Expected<NumericVariable *> ParseResult =
- parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM);
+ Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition(
+ DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM);
if (!ParseResult)
return ParseResult.takeError();
DefinedNumericVariable = *ParseResult;
}
- return std::move(ExpressionASTPointer);
+ return std::move(ExpressionPointer);
}
bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
@@ -476,10 +1002,10 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
}
// Parse numeric substitution block.
- std::unique_ptr<ExpressionAST> ExpressionASTPointer;
+ std::unique_ptr<Expression> ExpressionPointer;
Optional<NumericVariable *> DefinedNumericVariable;
if (IsNumBlock) {
- Expected<std::unique_ptr<ExpressionAST>> ParseResult =
+ Expected<std::unique_ptr<Expression>> ParseResult =
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
IsLegacyLineExpr, LineNumber, Context,
SM);
@@ -487,16 +1013,18 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
logAllUnhandledErrors(ParseResult.takeError(), errs());
return true;
}
- ExpressionASTPointer = std::move(*ParseResult);
- SubstNeeded = ExpressionASTPointer != nullptr;
+ ExpressionPointer = std::move(*ParseResult);
+ SubstNeeded = ExpressionPointer->getAST() != nullptr;
if (DefinedNumericVariable) {
IsDefinition = true;
DefName = (*DefinedNumericVariable)->getName();
}
if (SubstNeeded)
SubstStr = MatchStr;
- else
- MatchRegexp = "[0-9]+";
+ else {
+ ExpressionFormat Format = ExpressionPointer->getFormat();
+ MatchRegexp = cantFail(Format.getWildcardRegex());
+ }
}
// Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].
@@ -554,8 +1082,7 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
Substitution *Substitution =
IsNumBlock
? Context->makeNumericSubstitution(
- SubstStr, std::move(ExpressionASTPointer),
- SubstInsertIdx)
+ SubstStr, std::move(ExpressionPointer), SubstInsertIdx)
: Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
Substitutions.push_back(Substitution);
}
@@ -626,7 +1153,7 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
if (!Substitutions.empty()) {
TmpStr = RegExStr;
if (LineNumber)
- Context->LineVariable->setValue(*LineNumber);
+ Context->LineVariable->setValue(ExpressionValue(*LineNumber));
size_t InsertOffset = 0;
// Substitute all string variables and expressions whose values are only
@@ -635,8 +1162,18 @@ Expected<size_t> Pattern::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)
- return Value.takeError();
+ if (!Value) {
+ // Convert to an ErrorDiagnostic to get location information. This is
+ // done here rather than PrintNoMatch since now we know which
+ // substitution block caused the overflow.
+ Error Err =
+ handleErrors(Value.takeError(), [&](const OverflowError &E) {
+ return ErrorDiagnostic::get(SM, Substitution->getFromString(),
+ "unable to substitute variable or "
+ "numeric expression: overflow error");
+ });
+ return std::move(Err);
+ }
// Plop it into the regex at the adjusted offset.
TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset,
@@ -676,11 +1213,12 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
NumericVariableMatch.DefinedNumericVariable;
StringRef MatchedValue = MatchInfo[CaptureParenGroup];
- uint64_t Val;
- if (MatchedValue.getAsInteger(10, Val))
- return ErrorDiagnostic::get(SM, MatchedValue,
- "Unable to represent numeric value");
- DefinedNumericVariable->setValue(Val);
+ ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat();
+ Expected<ExpressionValue> Value =
+ Format.valueFromStringRepr(MatchedValue, SM);
+ if (!Value)
+ return Value.takeError();
+ DefinedNumericVariable->setValue(*Value);
}
// Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
@@ -721,17 +1259,20 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
// variables it uses.
if (!MatchedValue) {
bool UndefSeen = false;
- handleAllErrors(MatchedValue.takeError(), [](const NotFoundError &E) {},
- // Handled in PrintNoMatch().
- [](const ErrorDiagnostic &E) {},
- [&](const UndefVarError &E) {
- if (!UndefSeen) {
- OS << "uses undefined variable(s):";
- UndefSeen = true;
- }
- OS << " ";
- E.log(OS);
- });
+ handleAllErrors(
+ MatchedValue.takeError(), [](const NotFoundError &E) {},
+ // Handled in PrintNoMatch().
+ [](const ErrorDiagnostic &E) {},
+ // Handled in match().
+ [](const OverflowError &E) {},
+ [&](const UndefVarError &E) {
+ if (!UndefSeen) {
+ OS << "uses undefined variable(s):";
+ UndefSeen = true;
+ }
+ OS << " ";
+ E.log(OS);
+ });
} else {
// Substitution succeeded. Print substituted value.
OS << "with \"";
@@ -837,10 +1378,10 @@ FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
}
Substitution *FileCheckPatternContext::makeNumericSubstitution(
- StringRef ExpressionStr,
- std::unique_ptr<ExpressionAST> ExpressionASTPointer, size_t InsertIdx) {
+ StringRef ExpressionStr, std::unique_ptr<Expression> Expression,
+ size_t InsertIdx) {
Substitutions.push_back(std::make_unique<NumericSubstitution>(
- this, ExpressionStr, std::move(ExpressionASTPointer), InsertIdx));
+ this, ExpressionStr, std::move(Expression), InsertIdx));
return Substitutions.back().get();
}
@@ -915,20 +1456,17 @@ FileCheckDiag::FileCheckDiag(const SourceMgr &SM,
const Check::FileCheckType &CheckTy,
SMLoc CheckLoc, MatchType MatchTy,
SMRange InputRange)
- : CheckTy(CheckTy), MatchTy(MatchTy) {
+ : CheckTy(CheckTy), CheckLoc(CheckLoc), MatchTy(MatchTy) {
auto Start = SM.getLineAndColumn(InputRange.Start);
auto End = SM.getLineAndColumn(InputRange.End);
InputStartLine = Start.first;
InputStartCol = Start.second;
InputEndLine = End.first;
InputEndCol = End.second;
- Start = SM.getLineAndColumn(CheckLoc);
- CheckLine = Start.first;
- CheckCol = Start.second;
}
static bool IsPartOfWord(char c) {
- return (isalnum(c) || c == '-' || c == '_');
+ return (isAlnum(c) || c == '-' || c == '_');
}
Check::FileCheckType &Check::FileCheckType::setCount(int C) {
@@ -946,7 +1484,7 @@ std::string Check::FileCheckType::getDescription(StringRef Prefix) const {
case Check::CheckPlain:
if (Count > 1)
return Prefix.str() + "-COUNT";
- return Prefix;
+ return std::string(Prefix);
case Check::CheckNext:
return Prefix.str() + "-NEXT";
case Check::CheckSame:
@@ -959,6 +1497,8 @@ std::string Check::FileCheckType::getDescription(StringRef Prefix) const {
return Prefix.str() + "-LABEL";
case Check::CheckEmpty:
return Prefix.str() + "-EMPTY";
+ case Check::CheckComment:
+ return std::string(Prefix);
case Check::CheckEOF:
return "implicit EOF";
case Check::CheckBadNot:
@@ -970,13 +1510,24 @@ std::string Check::FileCheckType::getDescription(StringRef Prefix) const {
}
static std::pair<Check::FileCheckType, StringRef>
-FindCheckType(StringRef Buffer, StringRef Prefix) {
+FindCheckType(const FileCheckRequest &Req, StringRef Buffer, StringRef Prefix) {
if (Buffer.size() <= Prefix.size())
return {Check::CheckNone, StringRef()};
char NextChar = Buffer[Prefix.size()];
StringRef Rest = Buffer.drop_front(Prefix.size() + 1);
+
+ // Check for comment.
+ if (Req.CommentPrefixes.end() != std::find(Req.CommentPrefixes.begin(),
+ Req.CommentPrefixes.end(),
+ Prefix)) {
+ if (NextChar == ':')
+ return {Check::CheckComment, Rest};
+ // Ignore a comment prefix if it has a suffix like "-NOT".
+ return {Check::CheckNone, StringRef()};
+ }
+
// Verify that the : is present after the prefix.
if (NextChar == ':')
return {Check::CheckPlain, Rest};
@@ -1055,8 +1606,9 @@ static size_t SkipWord(StringRef Str, size_t Loc) {
/// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy
/// is unspecified.
static std::pair<StringRef, StringRef>
-FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
- unsigned &LineNumber, Check::FileCheckType &CheckTy) {
+FindFirstMatchingPrefix(const FileCheckRequest &Req, Regex &PrefixRE,
+ StringRef &Buffer, unsigned &LineNumber,
+ Check::FileCheckType &CheckTy) {
SmallVector<StringRef, 2> Matches;
while (!Buffer.empty()) {
@@ -1084,7 +1636,7 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
if (Skipped.empty() || !IsPartOfWord(Skipped.back())) {
// Now extract the type.
StringRef AfterSuffix;
- std::tie(CheckTy, AfterSuffix) = FindCheckType(Buffer, Prefix);
+ std::tie(CheckTy, AfterSuffix) = FindCheckType(Req, Buffer, Prefix);
// If we've found a valid check type for this prefix, we're done.
if (CheckTy != Check::CheckNone)
@@ -1104,7 +1656,8 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
void FileCheckPatternContext::createLineVariable() {
assert(!LineVariable && "@LINE pseudo numeric variable already created");
StringRef LineName = "@LINE";
- LineVariable = makeNumericVariable(LineName);
+ LineVariable = makeNumericVariable(
+ LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
GlobalNumericVariableTable[LineName] = LineVariable;
}
@@ -1114,8 +1667,12 @@ FileCheck::FileCheck(FileCheckRequest Req)
FileCheck::~FileCheck() = default;
-bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,
- Regex &PrefixRE) {
+bool FileCheck::readCheckFile(
+ SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
+ std::pair<unsigned, unsigned> *ImpPatBufferIDRange) {
+ if (ImpPatBufferIDRange)
+ ImpPatBufferIDRange->first = ImpPatBufferIDRange->second = 0;
+
Error DefineError =
PatternContext->defineCmdlineVariables(Req.GlobalDefines, SM);
if (DefineError) {
@@ -1126,17 +1683,27 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,
PatternContext->createLineVariable();
std::vector<Pattern> ImplicitNegativeChecks;
- for (const auto &PatternString : Req.ImplicitCheckNot) {
+ for (StringRef PatternString : Req.ImplicitCheckNot) {
// Create a buffer with fake command line content in order to display the
// command line option responsible for the specific implicit CHECK-NOT.
std::string Prefix = "-implicit-check-not='";
std::string Suffix = "'";
std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy(
- Prefix + PatternString + Suffix, "command line");
+ (Prefix + PatternString + Suffix).str(), "command line");
StringRef PatternInBuffer =
CmdLine->getBuffer().substr(Prefix.size(), PatternString.size());
- SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
+ unsigned BufferID = SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc());
+ if (ImpPatBufferIDRange) {
+ if (ImpPatBufferIDRange->first == ImpPatBufferIDRange->second) {
+ ImpPatBufferIDRange->first = BufferID;
+ ImpPatBufferIDRange->second = BufferID + 1;
+ } else {
+ assert(BufferID == ImpPatBufferIDRange->second &&
+ "expected consecutive source buffer IDs");
+ ++ImpPatBufferIDRange->second;
+ }
+ }
ImplicitNegativeChecks.push_back(
Pattern(Check::CheckNot, PatternContext.get()));
@@ -1150,6 +1717,7 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,
// found.
unsigned LineNumber = 1;
+ bool FoundUsedCheckPrefix = false;
while (1) {
Check::FileCheckType CheckTy;
@@ -1157,9 +1725,12 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,
StringRef UsedPrefix;
StringRef AfterSuffix;
std::tie(UsedPrefix, AfterSuffix) =
- FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber, CheckTy);
+ FindFirstMatchingPrefix(Req, PrefixRE, Buffer, LineNumber, CheckTy);
if (UsedPrefix.empty())
break;
+ if (CheckTy != Check::CheckComment)
+ FoundUsedCheckPrefix = true;
+
assert(UsedPrefix.data() == Buffer.data() &&
"Failed to move Buffer's start forward, or pointed prefix outside "
"of the buffer!");
@@ -1201,9 +1772,17 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,
// Remember the location of the start of the pattern, for diagnostics.
SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data());
+ // Extract the pattern from the buffer.
+ StringRef PatternBuffer = Buffer.substr(0, EOL);
+ Buffer = Buffer.substr(EOL);
+
+ // If this is a comment, we're done.
+ if (CheckTy == Check::CheckComment)
+ continue;
+
// Parse the pattern.
Pattern P(CheckTy, PatternContext.get(), LineNumber);
- if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req))
+ if (P.parsePattern(PatternBuffer, UsedPrefix, SM, Req))
return true;
// Verify that CHECK-LABEL lines do not define or use variables
@@ -1215,8 +1794,6 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,
return true;
}
- Buffer = Buffer.substr(EOL);
-
// 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) &&
@@ -1243,31 +1820,30 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,
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(
- Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1),
- *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
- std::swap(DagNotMatches, CheckStrings->back().DagNotStrings);
- }
-
- if (CheckStrings->empty()) {
+ // When there are no used prefixes we report an error except in the case that
+ // no prefix is specified explicitly but -implicit-check-not is specified.
+ if (!FoundUsedCheckPrefix &&
+ (ImplicitNegativeChecks.empty() || !Req.IsDefaultCheckPrefix)) {
errs() << "error: no check strings found with prefix"
<< (Req.CheckPrefixes.size() > 1 ? "es " : " ");
- auto I = Req.CheckPrefixes.begin();
- auto E = Req.CheckPrefixes.end();
- if (I != E) {
- errs() << "\'" << *I << ":'";
- ++I;
+ for (size_t I = 0, E = Req.CheckPrefixes.size(); I != E; ++I) {
+ if (I != 0)
+ errs() << ", ";
+ errs() << "\'" << Req.CheckPrefixes[I] << ":'";
}
- for (; I != E; ++I)
- errs() << ", \'" << *I << ":'";
-
errs() << '\n';
return true;
}
+ // Add an EOF pattern for any trailing --implicit-check-not/CHECK-DAG/-NOTs,
+ // and use the first prefix as a filler for the error message.
+ if (!DagNotMatches.empty()) {
+ CheckStrings->emplace_back(
+ Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1),
+ *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
+ std::swap(DagNotMatches, CheckStrings->back().DagNotStrings);
+ }
+
return false;
}
@@ -1706,43 +2282,74 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
return StartPos;
}
-// A check prefix must contain only alphanumeric, hyphens and underscores.
-static bool ValidateCheckPrefix(StringRef CheckPrefix) {
- static const Regex Validator("^[a-zA-Z0-9_-]*$");
- return Validator.match(CheckPrefix);
-}
-
-bool FileCheck::ValidateCheckPrefixes() {
- StringSet<> PrefixSet;
-
- for (StringRef Prefix : Req.CheckPrefixes) {
- // Reject empty prefixes.
- if (Prefix == "")
+static bool ValidatePrefixes(StringRef Kind, StringSet<> &UniquePrefixes,
+ ArrayRef<StringRef> SuppliedPrefixes) {
+ for (StringRef Prefix : SuppliedPrefixes) {
+ if (Prefix.empty()) {
+ errs() << "error: supplied " << Kind << " prefix must not be the empty "
+ << "string\n";
return false;
-
- if (!PrefixSet.insert(Prefix).second)
+ }
+ static const Regex Validator("^[a-zA-Z0-9_-]*$");
+ if (!Validator.match(Prefix)) {
+ errs() << "error: supplied " << Kind << " prefix must start with a "
+ << "letter and contain only alphanumeric characters, hyphens, and "
+ << "underscores: '" << Prefix << "'\n";
return false;
-
- if (!ValidateCheckPrefix(Prefix))
+ }
+ if (!UniquePrefixes.insert(Prefix).second) {
+ errs() << "error: supplied " << Kind << " prefix must be unique among "
+ << "check and comment prefixes: '" << Prefix << "'\n";
return false;
+ }
}
+ return true;
+}
+
+static const char *DefaultCheckPrefixes[] = {"CHECK"};
+static const char *DefaultCommentPrefixes[] = {"COM", "RUN"};
+bool FileCheck::ValidateCheckPrefixes() {
+ StringSet<> UniquePrefixes;
+ // Add default prefixes to catch user-supplied duplicates of them below.
+ if (Req.CheckPrefixes.empty()) {
+ for (const char *Prefix : DefaultCheckPrefixes)
+ UniquePrefixes.insert(Prefix);
+ }
+ if (Req.CommentPrefixes.empty()) {
+ for (const char *Prefix : DefaultCommentPrefixes)
+ UniquePrefixes.insert(Prefix);
+ }
+ // Do not validate the default prefixes, or diagnostics about duplicates might
+ // incorrectly indicate that they were supplied by the user.
+ if (!ValidatePrefixes("check", UniquePrefixes, Req.CheckPrefixes))
+ return false;
+ if (!ValidatePrefixes("comment", UniquePrefixes, Req.CommentPrefixes))
+ return false;
return true;
}
Regex FileCheck::buildCheckPrefixRegex() {
- // I don't think there's a way to specify an initial value for cl::list,
- // so if nothing was specified, add the default
- if (Req.CheckPrefixes.empty())
- Req.CheckPrefixes.push_back("CHECK");
+ if (Req.CheckPrefixes.empty()) {
+ for (const char *Prefix : DefaultCheckPrefixes)
+ Req.CheckPrefixes.push_back(Prefix);
+ Req.IsDefaultCheckPrefix = true;
+ }
+ if (Req.CommentPrefixes.empty()) {
+ for (const char *Prefix : DefaultCommentPrefixes)
+ Req.CommentPrefixes.push_back(Prefix);
+ }
- // We already validated the contents of CheckPrefixes so just concatenate
- // them as alternatives.
+ // We already validated the contents of CheckPrefixes and CommentPrefixes so
+ // just concatenate them as alternatives.
SmallString<32> PrefixRegexStr;
- for (StringRef Prefix : Req.CheckPrefixes) {
- if (Prefix != Req.CheckPrefixes.front())
+ for (size_t I = 0, E = Req.CheckPrefixes.size(); I != E; ++I) {
+ if (I != 0)
PrefixRegexStr.push_back('|');
-
+ PrefixRegexStr.append(Req.CheckPrefixes[I]);
+ }
+ for (StringRef Prefix : Req.CommentPrefixes) {
+ PrefixRegexStr.push_back('|');
PrefixRegexStr.append(Prefix);
}
@@ -1750,7 +2357,7 @@ Regex FileCheck::buildCheckPrefixRegex() {
}
Error FileCheckPatternContext::defineCmdlineVariables(
- std::vector<std::string> &CmdlineDefines, SourceMgr &SM) {
+ ArrayRef<StringRef> CmdlineDefines, SourceMgr &SM) {
assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&
"Overriding defined variable with command-line variable definitions");
@@ -1777,7 +2384,7 @@ Error FileCheckPatternContext::defineCmdlineVariables(
// format as in the input file to be able to reuse
// parseNumericSubstitutionBlock.
CmdlineDefsDiag += (DefPrefix + CmdlineDef + " (parsed as: [[").str();
- std::string SubstitutionStr = CmdlineDef;
+ std::string SubstitutionStr = std::string(CmdlineDef);
SubstitutionStr[EqIdx] = ':';
CmdlineDefsIndices.push_back(
std::make_pair(CmdlineDefsDiag.size(), SubstitutionStr.size()));
@@ -1815,20 +2422,19 @@ Error FileCheckPatternContext::defineCmdlineVariables(
// to create the necessary class instance.
StringRef CmdlineDefExpr = CmdlineDef.substr(1);
Optional<NumericVariable *> DefinedNumericVariable;
- Expected<std::unique_ptr<ExpressionAST>> ExpressionASTResult =
+ Expected<std::unique_ptr<Expression>> ExpressionResult =
Pattern::parseNumericSubstitutionBlock(
CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM);
- if (!ExpressionASTResult) {
- Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError());
+ if (!ExpressionResult) {
+ Errs = joinErrors(std::move(Errs), ExpressionResult.takeError());
continue;
}
- std::unique_ptr<ExpressionAST> ExpressionASTPointer =
- std::move(*ExpressionASTResult);
+ std::unique_ptr<Expression> Expression = std::move(*ExpressionResult);
// 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 = ExpressionASTPointer->eval();
+ Expected<ExpressionValue> Value = Expression->getAST()->eval();
if (!Value) {
Errs = joinErrors(std::move(Errs), Value.takeError());
continue;