aboutsummaryrefslogtreecommitdiff
path: root/llvm/unittests/Support/FileCheckTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/unittests/Support/FileCheckTest.cpp')
-rw-r--r--llvm/unittests/Support/FileCheckTest.cpp1724
1 files changed, 1724 insertions, 0 deletions
diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp
new file mode 100644
index 0000000..e986c43
--- /dev/null
+++ b/llvm/unittests/Support/FileCheckTest.cpp
@@ -0,0 +1,1724 @@
+//===- llvm/unittest/Support/FileCheckTest.cpp - FileCheck tests --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/FileCheck.h"
+#include "../lib/Support/FileCheckImpl.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+#include <tuple>
+#include <unordered_set>
+
+using namespace llvm;
+
+namespace {
+
+class FileCheckTest : public ::testing::Test {};
+
+static StringRef bufferize(SourceMgr &SM, StringRef Str) {
+ std::unique_ptr<MemoryBuffer> Buffer =
+ MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
+ StringRef StrBufferRef = Buffer->getBuffer();
+ SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
+ return StrBufferRef;
+}
+
+static std::string toString(const std::unordered_set<std::string> &Set) {
+ bool First = true;
+ std::string Str;
+ for (StringRef S : Set) {
+ Str += Twine(First ? "{" + S : ", " + S).str();
+ First = false;
+ }
+ Str += '}';
+ return Str;
+}
+
+template <typename ErrorT>
+static void expectSameErrors(std::unordered_set<std::string> ExpectedMsgs,
+ Error Err) {
+ auto AnyErrorMsgMatch = [&ExpectedMsgs](std::string &&ErrorMsg) -> bool {
+ for (auto ExpectedMsgItr = ExpectedMsgs.begin(),
+ ExpectedMsgEnd = ExpectedMsgs.end();
+ ExpectedMsgItr != ExpectedMsgEnd; ++ExpectedMsgItr) {
+ if (ErrorMsg.find(*ExpectedMsgItr) != std::string::npos) {
+ ExpectedMsgs.erase(ExpectedMsgItr);
+ return true;
+ }
+ }
+ return false;
+ };
+
+ Error RemainingErrors = std::move(Err);
+ do {
+ RemainingErrors =
+ handleErrors(std::move(RemainingErrors), [&](const ErrorT &E) {
+ EXPECT_TRUE(AnyErrorMsgMatch(E.message()))
+ << "Unexpected error message:" << std::endl
+ << E.message();
+ });
+ } while (RemainingErrors && !ExpectedMsgs.empty());
+ EXPECT_THAT_ERROR(std::move(RemainingErrors), Succeeded());
+ EXPECT_TRUE(ExpectedMsgs.empty())
+ << "Error message(s) not found:" << std::endl
+ << toString(ExpectedMsgs);
+}
+
+template <typename ErrorT>
+static void expectError(StringRef ExpectedMsg, Error Err) {
+ expectSameErrors<ErrorT>({ExpectedMsg.str()}, std::move(Err));
+}
+
+static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
+ expectError<ErrorDiagnostic>(ExpectedMsg, std::move(Err));
+}
+
+constexpr uint64_t MaxUint64 = std::numeric_limits<uint64_t>::max();
+constexpr int64_t MaxInt64 = std::numeric_limits<int64_t>::max();
+constexpr int64_t MinInt64 = std::numeric_limits<int64_t>::min();
+constexpr uint64_t AbsoluteMinInt64 =
+ static_cast<uint64_t>(-(MinInt64 + 1)) + 1;
+constexpr uint64_t AbsoluteMaxInt64 = static_cast<uint64_t>(MaxInt64);
+
+struct ExpressionFormatParameterisedFixture
+ : public ::testing::TestWithParam<
+ std::pair<ExpressionFormat::Kind, unsigned>> {
+ unsigned Precision;
+ bool Signed;
+ bool AllowHex;
+ bool AllowUpperHex;
+ ExpressionFormat Format;
+ Regex WildcardRegex;
+
+ StringRef TenStr;
+ StringRef FifteenStr;
+ std::string MaxUint64Str;
+ std::string MaxInt64Str;
+ std::string MinInt64Str;
+ StringRef FirstInvalidCharDigits;
+ StringRef AcceptedHexOnlyDigits;
+ StringRef RefusedHexOnlyDigits;
+
+ SourceMgr SM;
+
+ void SetUp() override {
+ ExpressionFormat::Kind Kind;
+ std::tie(Kind, Precision) = GetParam();
+ AllowHex = Kind == ExpressionFormat::Kind::HexLower ||
+ Kind == ExpressionFormat::Kind::HexUpper;
+ AllowUpperHex = Kind == ExpressionFormat::Kind::HexUpper;
+ Signed = Kind == ExpressionFormat::Kind::Signed;
+ Format = ExpressionFormat(Kind, Precision);
+
+ if (!AllowHex) {
+ MaxUint64Str = std::to_string(MaxUint64);
+ MaxInt64Str = std::to_string(MaxInt64);
+ MinInt64Str = std::to_string(MinInt64);
+ TenStr = "10";
+ FifteenStr = "15";
+ FirstInvalidCharDigits = "aA";
+ AcceptedHexOnlyDigits = RefusedHexOnlyDigits = "N/A";
+ return;
+ }
+
+ MaxUint64Str = AllowUpperHex ? "FFFFFFFFFFFFFFFF" : "ffffffffffffffff";
+ MaxInt64Str = AllowUpperHex ? "7FFFFFFFFFFFFFFF" : "7fffffffffffffff";
+ TenStr = AllowUpperHex ? "A" : "a";
+ FifteenStr = AllowUpperHex ? "F" : "f";
+ AcceptedHexOnlyDigits = AllowUpperHex ? "ABCDEF" : "abcdef";
+ RefusedHexOnlyDigits = AllowUpperHex ? "abcdef" : "ABCDEF";
+ MinInt64Str = "N/A";
+ FirstInvalidCharDigits = "gG";
+ }
+
+ void checkWildcardRegexMatch(StringRef Input,
+ unsigned TrailExtendTo = 0) const {
+ SmallVector<StringRef, 4> Matches;
+ std::string ExtendedInput = Input.str();
+ if (TrailExtendTo > Input.size()) {
+ ExtendedInput.append(TrailExtendTo - Input.size(), Input[0]);
+ }
+ ASSERT_TRUE(WildcardRegex.match(ExtendedInput, &Matches))
+ << "Wildcard regex does not match " << ExtendedInput;
+ EXPECT_EQ(Matches[0], ExtendedInput);
+ }
+
+ void checkWildcardRegexMatchFailure(StringRef Input) const {
+ EXPECT_FALSE(WildcardRegex.match(Input));
+ }
+
+ void checkWildcardRegexCharMatchFailure(StringRef Chars) const {
+ for (auto C : Chars)
+ EXPECT_FALSE(WildcardRegex.match(StringRef(&C, 1)));
+ }
+
+ std::string padWithLeadingZeros(StringRef NumStr) const {
+ bool Negative = NumStr.startswith("-");
+ if (NumStr.size() - unsigned(Negative) >= Precision)
+ return NumStr.str();
+
+ std::string PaddedStr;
+ if (Negative) {
+ PaddedStr = "-";
+ NumStr = NumStr.drop_front();
+ }
+ PaddedStr.append(Precision - NumStr.size(), '0');
+ PaddedStr.append(NumStr.str());
+ return PaddedStr;
+ }
+
+ template <class T> void checkMatchingString(T Val, StringRef ExpectedStr) {
+ Expected<std::string> MatchingString =
+ Format.getMatchingString(ExpressionValue(Val));
+ ASSERT_THAT_EXPECTED(MatchingString, Succeeded())
+ << "No matching string for " << Val;
+ EXPECT_EQ(*MatchingString, ExpectedStr);
+ }
+
+ template <class T> void checkMatchingStringFailure(T Val) {
+ Expected<std::string> MatchingString =
+ Format.getMatchingString(ExpressionValue(Val));
+ // Error message tested in ExpressionValue unit tests.
+ EXPECT_THAT_EXPECTED(MatchingString, Failed());
+ }
+
+ Expected<ExpressionValue> getValueFromStringReprFailure(StringRef Str) {
+ StringRef BufferizedStr = bufferize(SM, Str);
+ return Format.valueFromStringRepr(BufferizedStr, SM);
+ }
+
+ template <class T>
+ void checkValueFromStringRepr(StringRef Str, T ExpectedVal) {
+ Expected<ExpressionValue> ResultValue = getValueFromStringReprFailure(Str);
+ ASSERT_THAT_EXPECTED(ResultValue, Succeeded())
+ << "Failed to get value from " << Str;
+ ASSERT_EQ(ResultValue->isNegative(), ExpectedVal < 0)
+ << "Value for " << Str << " is not " << ExpectedVal;
+ if (ResultValue->isNegative())
+ EXPECT_EQ(cantFail(ResultValue->getSignedValue()),
+ static_cast<int64_t>(ExpectedVal));
+ else
+ EXPECT_EQ(cantFail(ResultValue->getUnsignedValue()),
+ static_cast<uint64_t>(ExpectedVal));
+ }
+
+ void checkValueFromStringReprFailure(StringRef Str) {
+ StringRef OverflowErrorStr = "unable to represent numeric value";
+ Expected<ExpressionValue> ResultValue = getValueFromStringReprFailure(Str);
+ expectDiagnosticError(OverflowErrorStr, ResultValue.takeError());
+ }
+};
+
+TEST_P(ExpressionFormatParameterisedFixture, FormatGetWildcardRegex) {
+ // Wildcard regex is valid.
+ Expected<std::string> WildcardPattern = Format.getWildcardRegex();
+ ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded());
+ WildcardRegex = Regex((Twine("^") + *WildcardPattern + "$").str());
+ ASSERT_TRUE(WildcardRegex.isValid());
+
+ // Does not match empty string.
+ checkWildcardRegexMatchFailure("");
+
+ // Matches all decimal digits and matches several of them.
+ StringRef LongNumber = "12345678901234567890";
+ checkWildcardRegexMatch(LongNumber);
+
+ // Matches negative digits.
+ LongNumber = "-12345678901234567890";
+ if (Signed)
+ checkWildcardRegexMatch(LongNumber);
+ else
+ checkWildcardRegexMatchFailure(LongNumber);
+
+ // Check non digits or digits with wrong casing are not matched.
+ if (AllowHex) {
+ checkWildcardRegexMatch(AcceptedHexOnlyDigits, 16);
+ checkWildcardRegexCharMatchFailure(RefusedHexOnlyDigits);
+ }
+ checkWildcardRegexCharMatchFailure(FirstInvalidCharDigits);
+
+ // Check leading zeros are only accepted if number of digits is less than the
+ // precision.
+ LongNumber = "01234567890123456789";
+ if (Precision) {
+ checkWildcardRegexMatch(LongNumber.take_front(Precision));
+ checkWildcardRegexMatchFailure(LongNumber.take_front(Precision - 1));
+ if (Precision < LongNumber.size())
+ checkWildcardRegexMatchFailure(LongNumber.take_front(Precision + 1));
+ } else
+ checkWildcardRegexMatch(LongNumber);
+}
+
+TEST_P(ExpressionFormatParameterisedFixture, FormatGetMatchingString) {
+ checkMatchingString(0, padWithLeadingZeros("0"));
+ checkMatchingString(9, padWithLeadingZeros("9"));
+
+ if (Signed) {
+ checkMatchingString(-5, padWithLeadingZeros("-5"));
+ checkMatchingStringFailure(MaxUint64);
+ checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str));
+ checkMatchingString(MinInt64, padWithLeadingZeros(MinInt64Str));
+ } else {
+ checkMatchingStringFailure(-5);
+ checkMatchingString(MaxUint64, padWithLeadingZeros(MaxUint64Str));
+ checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str));
+ checkMatchingStringFailure(MinInt64);
+ }
+
+ checkMatchingString(10, padWithLeadingZeros(TenStr));
+ checkMatchingString(15, padWithLeadingZeros(FifteenStr));
+}
+
+TEST_P(ExpressionFormatParameterisedFixture, FormatValueFromStringRepr) {
+ checkValueFromStringRepr("0", 0);
+ checkValueFromStringRepr("9", 9);
+
+ if (Signed) {
+ checkValueFromStringRepr("-5", -5);
+ checkValueFromStringReprFailure(MaxUint64Str);
+ } else {
+ checkValueFromStringReprFailure("-5");
+ checkValueFromStringRepr(MaxUint64Str, MaxUint64);
+ }
+
+ checkValueFromStringRepr(TenStr, 10);
+ checkValueFromStringRepr(FifteenStr, 15);
+
+ // Wrong casing is not tested because valueFromStringRepr() relies on
+ // StringRef's getAsInteger() which does not allow to restrict casing.
+ checkValueFromStringReprFailure("G");
+}
+
+TEST_P(ExpressionFormatParameterisedFixture, FormatBoolOperator) {
+ EXPECT_TRUE(bool(Format));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture,
+ ::testing::Values(std::make_pair(ExpressionFormat::Kind::Unsigned, 0),
+ std::make_pair(ExpressionFormat::Kind::Signed, 0),
+ std::make_pair(ExpressionFormat::Kind::HexLower, 0),
+ std::make_pair(ExpressionFormat::Kind::HexUpper, 0),
+
+ std::make_pair(ExpressionFormat::Kind::Unsigned, 1),
+ std::make_pair(ExpressionFormat::Kind::Signed, 1),
+ std::make_pair(ExpressionFormat::Kind::HexLower, 1),
+ std::make_pair(ExpressionFormat::Kind::HexUpper, 1),
+
+ std::make_pair(ExpressionFormat::Kind::Unsigned, 16),
+ std::make_pair(ExpressionFormat::Kind::Signed, 16),
+ std::make_pair(ExpressionFormat::Kind::HexLower, 16),
+ std::make_pair(ExpressionFormat::Kind::HexUpper, 16),
+
+ std::make_pair(ExpressionFormat::Kind::Unsigned, 20),
+ std::make_pair(ExpressionFormat::Kind::Signed, 20)), );
+
+TEST_F(FileCheckTest, NoFormatProperties) {
+ ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
+ expectError<StringError>("trying to match value with invalid format",
+ NoFormat.getWildcardRegex().takeError());
+ expectError<StringError>(
+ "trying to match value with invalid format",
+ NoFormat.getMatchingString(ExpressionValue(18u)).takeError());
+ EXPECT_FALSE(bool(NoFormat));
+}
+
+TEST_F(FileCheckTest, FormatEqualityOperators) {
+ ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
+ ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned);
+ EXPECT_TRUE(UnsignedFormat == UnsignedFormat2);
+ EXPECT_FALSE(UnsignedFormat != UnsignedFormat2);
+
+ ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower);
+ EXPECT_FALSE(UnsignedFormat == HexLowerFormat);
+ EXPECT_TRUE(UnsignedFormat != HexLowerFormat);
+
+ ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
+ ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat);
+ EXPECT_FALSE(NoFormat == NoFormat2);
+ EXPECT_TRUE(NoFormat != NoFormat2);
+}
+
+TEST_F(FileCheckTest, FormatKindEqualityOperators) {
+ ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
+ EXPECT_TRUE(UnsignedFormat == ExpressionFormat::Kind::Unsigned);
+ EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned);
+ EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower);
+ EXPECT_TRUE(UnsignedFormat != ExpressionFormat::Kind::HexLower);
+ ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
+ EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat);
+ EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);
+}
+
+template <class T1, class T2>
+static Expected<ExpressionValue> doValueOperation(binop_eval_t Operation,
+ T1 LeftValue, T2 RightValue) {
+ ExpressionValue LeftOperand(LeftValue);
+ ExpressionValue RightOperand(RightValue);
+ return Operation(LeftOperand, RightOperand);
+}
+
+template <class T>
+static void expectValueEqual(ExpressionValue ActualValue, T ExpectedValue) {
+ EXPECT_EQ(ExpectedValue < 0, ActualValue.isNegative());
+ if (ExpectedValue < 0) {
+ Expected<int64_t> SignedActualValue = ActualValue.getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedActualValue, Succeeded());
+ EXPECT_EQ(*SignedActualValue, static_cast<int64_t>(ExpectedValue));
+ } else {
+ Expected<uint64_t> UnsignedActualValue = ActualValue.getUnsignedValue();
+ ASSERT_THAT_EXPECTED(UnsignedActualValue, Succeeded());
+ EXPECT_EQ(*UnsignedActualValue, static_cast<uint64_t>(ExpectedValue));
+ }
+}
+
+template <class T1, class T2, class TR>
+static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue,
+ T2 RightValue, TR ResultValue) {
+ Expected<ExpressionValue> OperationResult =
+ doValueOperation(Operation, LeftValue, RightValue);
+ ASSERT_THAT_EXPECTED(OperationResult, Succeeded());
+ expectValueEqual(*OperationResult, ResultValue);
+}
+
+template <class T1, class T2>
+static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue,
+ T2 RightValue) {
+ expectError<OverflowError>(
+ "overflow error",
+ doValueOperation(Operation, LeftValue, RightValue).takeError());
+}
+
+TEST_F(FileCheckTest, ExpressionValueGetUnsigned) {
+ // Test positive value.
+ Expected<uint64_t> UnsignedValue = ExpressionValue(10).getUnsignedValue();
+ ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded());
+ EXPECT_EQ(*UnsignedValue, 10U);
+
+ // Test 0.
+ UnsignedValue = ExpressionValue(0).getUnsignedValue();
+ ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded());
+ EXPECT_EQ(*UnsignedValue, 0U);
+
+ // Test max positive value.
+ UnsignedValue = ExpressionValue(MaxUint64).getUnsignedValue();
+ ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded());
+ EXPECT_EQ(*UnsignedValue, MaxUint64);
+
+ // Test failure with negative value.
+ expectError<OverflowError>(
+ "overflow error", ExpressionValue(-1).getUnsignedValue().takeError());
+
+ // Test failure with min negative value.
+ expectError<OverflowError>(
+ "overflow error",
+ ExpressionValue(MinInt64).getUnsignedValue().takeError());
+}
+
+TEST_F(FileCheckTest, ExpressionValueGetSigned) {
+ // Test positive value.
+ Expected<int64_t> SignedValue = ExpressionValue(10).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, 10);
+
+ // Test 0.
+ SignedValue = ExpressionValue(0).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, 0);
+
+ // Test max int64_t.
+ SignedValue = ExpressionValue(MaxInt64).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, MaxInt64);
+
+ // Test failure with too big positive value.
+ expectError<OverflowError>(
+ "overflow error", ExpressionValue(static_cast<uint64_t>(MaxInt64) + 1)
+ .getSignedValue()
+ .takeError());
+
+ // Test failure with max uint64_t.
+ expectError<OverflowError>(
+ "overflow error",
+ ExpressionValue(MaxUint64).getSignedValue().takeError());
+
+ // Test negative value.
+ SignedValue = ExpressionValue(-10).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, -10);
+
+ // Test min int64_t.
+ SignedValue = ExpressionValue(MinInt64).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, MinInt64);
+}
+
+TEST_F(FileCheckTest, ExpressionValueAbsolute) {
+ // Test positive value.
+ expectValueEqual(ExpressionValue(10).getAbsolute(), 10);
+
+ // Test 0.
+ expectValueEqual(ExpressionValue(0).getAbsolute(), 0);
+
+ // Test max uint64_t.
+ expectValueEqual(ExpressionValue(MaxUint64).getAbsolute(), MaxUint64);
+
+ // Test negative value.
+ expectValueEqual(ExpressionValue(-10).getAbsolute(), 10);
+
+ // Test absence of overflow on min int64_t.
+ expectValueEqual(ExpressionValue(MinInt64).getAbsolute(),
+ static_cast<uint64_t>(-(MinInt64 + 10)) + 10);
+}
+
+TEST_F(FileCheckTest, ExpressionValueAddition) {
+ // Test both negative values.
+ expectOperationValueResult(operator+, -10, -10, -20);
+
+ // Test both negative values with underflow.
+ expectOperationValueResult(operator+, MinInt64, -1);
+ expectOperationValueResult(operator+, MinInt64, MinInt64);
+
+ // Test negative and positive value.
+ expectOperationValueResult(operator+, -10, 10, 0);
+ expectOperationValueResult(operator+, -10, 11, 1);
+ expectOperationValueResult(operator+, -11, 10, -1);
+
+ // Test positive and negative value.
+ expectOperationValueResult(operator+, 10, -10, 0);
+ expectOperationValueResult(operator+, 10, -11, -1);
+ expectOperationValueResult(operator+, 11, -10, 1);
+
+ // Test both positive values.
+ expectOperationValueResult(operator+, 10, 10, 20);
+
+ // Test both positive values with overflow.
+ expectOperationValueResult(operator+, MaxUint64, 1);
+ expectOperationValueResult(operator+, MaxUint64, MaxUint64);
+}
+
+TEST_F(FileCheckTest, ExpressionValueSubtraction) {
+ // Test negative value and value bigger than int64_t max.
+ expectOperationValueResult(operator-, -10, MaxUint64);
+
+ // Test negative and positive value with underflow.
+ expectOperationValueResult(operator-, MinInt64, 1);
+
+ // Test negative and positive value.
+ expectOperationValueResult(operator-, -10, 10, -20);
+
+ // Test both negative values.
+ expectOperationValueResult(operator-, -10, -10, 0);
+ expectOperationValueResult(operator-, -11, -10, -1);
+ expectOperationValueResult(operator-, -10, -11, 1);
+
+ // Test positive and negative values.
+ expectOperationValueResult(operator-, 10, -10, 20);
+
+ // Test both positive values with result positive.
+ expectOperationValueResult(operator-, 10, 5, 5);
+
+ // Test both positive values with underflow.
+ expectOperationValueResult(operator-, 0, MaxUint64);
+ expectOperationValueResult(operator-, 0,
+ static_cast<uint64_t>(-(MinInt64 + 10)) + 11);
+
+ // Test both positive values with result < -(max int64_t)
+ expectOperationValueResult(operator-, 10,
+ static_cast<uint64_t>(MaxInt64) + 11,
+ -MaxInt64 - 1);
+
+ // Test both positive values with 0 > result > -(max int64_t)
+ expectOperationValueResult(operator-, 10, 11, -1);
+}
+
+TEST_F(FileCheckTest, ExpressionValueMultiplication) {
+ // Test mixed signed values.
+ expectOperationValueResult(operator*, -3, 10, -30);
+ expectOperationValueResult(operator*, 2, -17, -34);
+ expectOperationValueResult(operator*, 0, MinInt64, 0);
+ expectOperationValueResult(operator*, MinInt64, 1, MinInt64);
+ expectOperationValueResult(operator*, 1, MinInt64, MinInt64);
+ expectOperationValueResult(operator*, MaxInt64, -1, -MaxInt64);
+ expectOperationValueResult(operator*, -1, MaxInt64, -MaxInt64);
+
+ // Test both negative values.
+ expectOperationValueResult(operator*, -3, -10, 30);
+ expectOperationValueResult(operator*, -2, -17, 34);
+ expectOperationValueResult(operator*, MinInt64, -1, AbsoluteMinInt64);
+
+ // Test both positive values.
+ expectOperationValueResult(operator*, 3, 10, 30);
+ expectOperationValueResult(operator*, 2, 17, 34);
+ expectOperationValueResult(operator*, 0, MaxUint64, 0);
+
+ // Test negative results that underflow.
+ expectOperationValueResult(operator*, -10, MaxInt64);
+ expectOperationValueResult(operator*, MaxInt64, -10);
+ expectOperationValueResult(operator*, 10, MinInt64);
+ expectOperationValueResult(operator*, MinInt64, 10);
+ expectOperationValueResult(operator*, -1, MaxUint64);
+ expectOperationValueResult(operator*, MaxUint64, -1);
+ expectOperationValueResult(operator*, -1, AbsoluteMaxInt64 + 2);
+ expectOperationValueResult(operator*, AbsoluteMaxInt64 + 2, -1);
+
+ // Test positive results that overflow.
+ expectOperationValueResult(operator*, 10, MaxUint64);
+ expectOperationValueResult(operator*, MaxUint64, 10);
+ expectOperationValueResult(operator*, MinInt64, -10);
+ expectOperationValueResult(operator*, -10, MinInt64);
+}
+
+TEST_F(FileCheckTest, ExpressionValueDivision) {
+ // Test mixed signed values.
+ expectOperationValueResult(operator/, -30, 10, -3);
+ expectOperationValueResult(operator/, 34, -17, -2);
+ expectOperationValueResult(operator/, 0, -10, 0);
+ expectOperationValueResult(operator/, MinInt64, 1, MinInt64);
+ expectOperationValueResult(operator/, MaxInt64, -1, -MaxInt64);
+ expectOperationValueResult(operator/, -MaxInt64, 1, -MaxInt64);
+
+ // Test both negative values.
+ expectOperationValueResult(operator/, -30, -10, 3);
+ expectOperationValueResult(operator/, -34, -17, 2);
+
+ // Test both positive values.
+ expectOperationValueResult(operator/, 30, 10, 3);
+ expectOperationValueResult(operator/, 34, 17, 2);
+ expectOperationValueResult(operator/, 0, 10, 0);
+
+ // Test divide by zero.
+ expectOperationValueResult(operator/, -10, 0);
+ expectOperationValueResult(operator/, 10, 0);
+ expectOperationValueResult(operator/, 0, 0);
+
+ // Test negative result that underflows.
+ expectOperationValueResult(operator/, MaxUint64, -1);
+ expectOperationValueResult(operator/, AbsoluteMaxInt64 + 2, -1);
+}
+
+TEST_F(FileCheckTest, ExpressionValueEquality) {
+ // Test negative and positive value.
+ EXPECT_FALSE(ExpressionValue(5) == ExpressionValue(-3));
+ EXPECT_TRUE(ExpressionValue(5) != ExpressionValue(-3));
+ EXPECT_FALSE(ExpressionValue(-2) == ExpressionValue(6));
+ EXPECT_TRUE(ExpressionValue(-2) != ExpressionValue(6));
+ EXPECT_FALSE(ExpressionValue(-7) == ExpressionValue(7));
+ EXPECT_TRUE(ExpressionValue(-7) != ExpressionValue(7));
+ EXPECT_FALSE(ExpressionValue(4) == ExpressionValue(-4));
+ EXPECT_TRUE(ExpressionValue(4) != ExpressionValue(-4));
+ EXPECT_FALSE(ExpressionValue(MaxUint64) == ExpressionValue(-1));
+ EXPECT_TRUE(ExpressionValue(MaxUint64) != ExpressionValue(-1));
+
+ // Test both negative values.
+ EXPECT_FALSE(ExpressionValue(-2) == ExpressionValue(-7));
+ EXPECT_TRUE(ExpressionValue(-2) != ExpressionValue(-7));
+ EXPECT_TRUE(ExpressionValue(-3) == ExpressionValue(-3));
+ EXPECT_FALSE(ExpressionValue(-3) != ExpressionValue(-3));
+ EXPECT_FALSE(ExpressionValue(MinInt64) == ExpressionValue(-1));
+ EXPECT_TRUE(ExpressionValue(MinInt64) != ExpressionValue(-1));
+ EXPECT_FALSE(ExpressionValue(MinInt64) == ExpressionValue(-0));
+ EXPECT_TRUE(ExpressionValue(MinInt64) != ExpressionValue(-0));
+
+ // Test both positive values.
+ EXPECT_FALSE(ExpressionValue(8) == ExpressionValue(9));
+ EXPECT_TRUE(ExpressionValue(8) != ExpressionValue(9));
+ EXPECT_TRUE(ExpressionValue(1) == ExpressionValue(1));
+ EXPECT_FALSE(ExpressionValue(1) != ExpressionValue(1));
+
+ // Check the signedness of zero doesn't affect equality.
+ EXPECT_TRUE(ExpressionValue(0) == ExpressionValue(0));
+ EXPECT_FALSE(ExpressionValue(0) != ExpressionValue(0));
+ EXPECT_TRUE(ExpressionValue(0) == ExpressionValue(-0));
+ EXPECT_FALSE(ExpressionValue(0) != ExpressionValue(-0));
+ EXPECT_TRUE(ExpressionValue(-0) == ExpressionValue(0));
+ EXPECT_FALSE(ExpressionValue(-0) != ExpressionValue(0));
+ EXPECT_TRUE(ExpressionValue(-0) == ExpressionValue(-0));
+ EXPECT_FALSE(ExpressionValue(-0) != ExpressionValue(-0));
+}
+
+TEST_F(FileCheckTest, Literal) {
+ SourceMgr SM;
+
+ // Eval returns the literal's value.
+ ExpressionLiteral Ten(bufferize(SM, "10"), 10u);
+ Expected<ExpressionValue> Value = Ten.eval();
+ ASSERT_THAT_EXPECTED(Value, Succeeded());
+ EXPECT_EQ(10, cantFail(Value->getSignedValue()));
+ Expected<ExpressionFormat> ImplicitFormat = Ten.getImplicitFormat(SM);
+ ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
+ EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::NoFormat);
+
+ // Min value can be correctly represented.
+ ExpressionLiteral Min(bufferize(SM, std::to_string(MinInt64)), MinInt64);
+ Value = Min.eval();
+ ASSERT_TRUE(bool(Value));
+ EXPECT_EQ(MinInt64, cantFail(Value->getSignedValue()));
+
+ // Max value can be correctly represented.
+ ExpressionLiteral Max(bufferize(SM, std::to_string(MaxUint64)), MaxUint64);
+ Value = Max.eval();
+ ASSERT_THAT_EXPECTED(Value, Succeeded());
+ EXPECT_EQ(MaxUint64, cantFail(Value->getUnsignedValue()));
+}
+
+TEST_F(FileCheckTest, Expression) {
+ SourceMgr SM;
+
+ std::unique_ptr<ExpressionLiteral> Ten =
+ std::make_unique<ExpressionLiteral>(bufferize(SM, "10"), 10u);
+ ExpressionLiteral *TenPtr = Ten.get();
+ Expression Expr(std::move(Ten),
+ ExpressionFormat(ExpressionFormat::Kind::HexLower));
+ EXPECT_EQ(Expr.getAST(), TenPtr);
+ EXPECT_EQ(Expr.getFormat(), ExpressionFormat::Kind::HexLower);
+}
+
+static void
+expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
+ Error Err) {
+ EXPECT_THAT_ERROR(handleErrors(std::move(Err),
+ [&](const UndefVarError &E) {
+ EXPECT_EQ(ExpectedUndefVarNames.erase(
+ std::string(E.getVarName())),
+ 1U);
+ }),
+ Succeeded());
+ EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
+}
+
+TEST_F(FileCheckTest, NumericVariable) {
+ SourceMgr SM;
+
+ // Undefined variable: getValue and eval fail, error returned by eval holds
+ // the name of the undefined variable.
+ NumericVariable FooVar("FOO",
+ ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
+ EXPECT_EQ("FOO", FooVar.getName());
+ EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
+ NumericVariableUse FooVarUse("FOO", &FooVar);
+ Expected<ExpressionFormat> ImplicitFormat = FooVarUse.getImplicitFormat(SM);
+ ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
+ EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);
+ EXPECT_FALSE(FooVar.getValue());
+ Expected<ExpressionValue> EvalResult = FooVarUse.eval();
+ expectUndefErrors({"FOO"}, EvalResult.takeError());
+
+ // Defined variable without string: only getValue and eval return value set.
+ FooVar.setValue(ExpressionValue(42u));
+ Optional<ExpressionValue> Value = FooVar.getValue();
+ ASSERT_TRUE(Value);
+ EXPECT_EQ(42, cantFail(Value->getSignedValue()));
+ EXPECT_FALSE(FooVar.getStringValue());
+ EvalResult = FooVarUse.eval();
+ ASSERT_THAT_EXPECTED(EvalResult, Succeeded());
+ EXPECT_EQ(42, cantFail(EvalResult->getSignedValue()));
+
+ // Defined variable with string: getValue, eval, and getStringValue return
+ // value set.
+ StringRef StringValue = "925";
+ FooVar.setValue(ExpressionValue(925u), StringValue);
+ Value = FooVar.getValue();
+ ASSERT_TRUE(Value);
+ EXPECT_EQ(925, cantFail(Value->getSignedValue()));
+ // getStringValue should return the same memory not just the same characters.
+ EXPECT_EQ(StringValue.begin(), FooVar.getStringValue().getValue().begin());
+ EXPECT_EQ(StringValue.end(), FooVar.getStringValue().getValue().end());
+ EvalResult = FooVarUse.eval();
+ ASSERT_THAT_EXPECTED(EvalResult, Succeeded());
+ EXPECT_EQ(925, cantFail(EvalResult->getSignedValue()));
+ EXPECT_EQ(925, cantFail(EvalResult->getSignedValue()));
+
+ // Clearing variable: getValue and eval fail. Error returned by eval holds
+ // the name of the cleared variable.
+ FooVar.clearValue();
+ EXPECT_FALSE(FooVar.getValue());
+ EXPECT_FALSE(FooVar.getStringValue());
+ EvalResult = FooVarUse.eval();
+ expectUndefErrors({"FOO"}, EvalResult.takeError());
+}
+
+TEST_F(FileCheckTest, Binop) {
+ SourceMgr SM;
+
+ StringRef ExprStr = bufferize(SM, "FOO+BAR");
+ StringRef FooStr = ExprStr.take_front(3);
+ NumericVariable FooVar(FooStr,
+ ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
+ FooVar.setValue(ExpressionValue(42u));
+ std::unique_ptr<NumericVariableUse> FooVarUse =
+ std::make_unique<NumericVariableUse>(FooStr, &FooVar);
+ StringRef BarStr = ExprStr.take_back(3);
+ NumericVariable BarVar(BarStr,
+ ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
+ BarVar.setValue(ExpressionValue(18u));
+ std::unique_ptr<NumericVariableUse> BarVarUse =
+ std::make_unique<NumericVariableUse>(BarStr, &BarVar);
+ binop_eval_t doAdd = operator+;
+ BinaryOperation Binop(ExprStr, doAdd, std::move(FooVarUse),
+ std::move(BarVarUse));
+
+ // Defined variables: eval returns right value; implicit format is as
+ // expected.
+ Expected<ExpressionValue> Value = Binop.eval();
+ ASSERT_THAT_EXPECTED(Value, Succeeded());
+ EXPECT_EQ(60, cantFail(Value->getSignedValue()));
+ Expected<ExpressionFormat> ImplicitFormat = Binop.getImplicitFormat(SM);
+ ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
+ EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);
+
+ // 1 undefined variable: eval fails, error contains name of undefined
+ // variable.
+ FooVar.clearValue();
+ Value = Binop.eval();
+ expectUndefErrors({"FOO"}, Value.takeError());
+
+ // 2 undefined variables: eval fails, error contains names of all undefined
+ // variables.
+ BarVar.clearValue();
+ Value = Binop.eval();
+ expectUndefErrors({"FOO", "BAR"}, Value.takeError());
+
+ // Literal + Variable has format of variable.
+ ExprStr = bufferize(SM, "FOO+18");
+ FooStr = ExprStr.take_front(3);
+ StringRef EighteenStr = ExprStr.take_back(2);
+ FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);
+ std::unique_ptr<ExpressionLiteral> Eighteen =
+ std::make_unique<ExpressionLiteral>(EighteenStr, 18u);
+ Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse),
+ std::move(Eighteen));
+ ImplicitFormat = Binop.getImplicitFormat(SM);
+ ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
+ EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);
+ ExprStr = bufferize(SM, "18+FOO");
+ FooStr = ExprStr.take_back(3);
+ EighteenStr = ExprStr.take_front(2);
+ FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);
+ Eighteen = std::make_unique<ExpressionLiteral>(EighteenStr, 18u);
+ Binop = BinaryOperation(ExprStr, doAdd, std::move(Eighteen),
+ std::move(FooVarUse));
+ ImplicitFormat = Binop.getImplicitFormat(SM);
+ ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
+ EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);
+
+ // Variables with different implicit format conflict.
+ ExprStr = bufferize(SM, "FOO+BAZ");
+ FooStr = ExprStr.take_front(3);
+ StringRef BazStr = ExprStr.take_back(3);
+ NumericVariable BazVar(BazStr,
+ ExpressionFormat(ExpressionFormat::Kind::HexLower), 3);
+ FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);
+ std::unique_ptr<NumericVariableUse> BazVarUse =
+ std::make_unique<NumericVariableUse>(BazStr, &BazVar);
+ Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse),
+ std::move(BazVarUse));
+ ImplicitFormat = Binop.getImplicitFormat(SM);
+ expectDiagnosticError(
+ "implicit format conflict between 'FOO' (%u) and 'BAZ' (%x), "
+ "need an explicit format specifier",
+ ImplicitFormat.takeError());
+
+ // All variable conflicts are reported.
+ ExprStr = bufferize(SM, "(FOO+BAZ)+(FOO+QUUX)");
+ StringRef Paren1ExprStr = ExprStr.substr(1, 7);
+ FooStr = Paren1ExprStr.take_front(3);
+ BazStr = Paren1ExprStr.take_back(3);
+ StringRef Paren2ExprStr = ExprStr.substr(ExprStr.rfind('(') + 1, 8);
+ StringRef FooStr2 = Paren2ExprStr.take_front(3);
+ StringRef QuuxStr = Paren2ExprStr.take_back(4);
+ FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);
+ BazVarUse = std::make_unique<NumericVariableUse>(BazStr, &BazVar);
+ std::unique_ptr<NumericVariableUse> FooVarUse2 =
+ std::make_unique<NumericVariableUse>(FooStr2, &FooVar);
+ NumericVariable QuuxVar(
+ QuuxStr, ExpressionFormat(ExpressionFormat::Kind::HexLower), 4);
+ std::unique_ptr<NumericVariableUse> QuuxVarUse =
+ std::make_unique<NumericVariableUse>(QuuxStr, &QuuxVar);
+ std::unique_ptr<BinaryOperation> Binop1 = std::make_unique<BinaryOperation>(
+ ExprStr.take_front(9), doAdd, std::move(FooVarUse), std::move(BazVarUse));
+ std::unique_ptr<BinaryOperation> Binop2 = std::make_unique<BinaryOperation>(
+ ExprStr.take_back(10), doAdd, std::move(FooVarUse2),
+ std::move(QuuxVarUse));
+ std::unique_ptr<BinaryOperation> OuterBinop =
+ std::make_unique<BinaryOperation>(ExprStr, doAdd, std::move(Binop1),
+ std::move(Binop2));
+ ImplicitFormat = OuterBinop->getImplicitFormat(SM);
+ expectSameErrors<ErrorDiagnostic>(
+ {("implicit format conflict between 'FOO' (%u) and 'BAZ' (%x), need an "
+ "explicit format specifier"),
+ ("implicit format conflict between 'FOO' (%u) and 'QUUX' (%x), need an "
+ "explicit format specifier")},
+ ImplicitFormat.takeError());
+}
+
+TEST_F(FileCheckTest, ValidVarNameStart) {
+ EXPECT_TRUE(Pattern::isValidVarNameStart('a'));
+ EXPECT_TRUE(Pattern::isValidVarNameStart('G'));
+ EXPECT_TRUE(Pattern::isValidVarNameStart('_'));
+ EXPECT_FALSE(Pattern::isValidVarNameStart('2'));
+ EXPECT_FALSE(Pattern::isValidVarNameStart('$'));
+ EXPECT_FALSE(Pattern::isValidVarNameStart('@'));
+ EXPECT_FALSE(Pattern::isValidVarNameStart('+'));
+ EXPECT_FALSE(Pattern::isValidVarNameStart('-'));
+ EXPECT_FALSE(Pattern::isValidVarNameStart(':'));
+}
+
+TEST_F(FileCheckTest, ParseVar) {
+ SourceMgr SM;
+ StringRef OrigVarName = bufferize(SM, "GoodVar42");
+ StringRef VarName = OrigVarName;
+ Expected<Pattern::VariableProperties> ParsedVarResult =
+ Pattern::parseVariable(VarName, SM);
+ ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
+ EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
+ EXPECT_TRUE(VarName.empty());
+ EXPECT_FALSE(ParsedVarResult->IsPseudo);
+
+ VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar");
+ ParsedVarResult = Pattern::parseVariable(VarName, SM);
+ ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
+ EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
+ EXPECT_TRUE(VarName.empty());
+ EXPECT_FALSE(ParsedVarResult->IsPseudo);
+
+ VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar");
+ ParsedVarResult = Pattern::parseVariable(VarName, SM);
+ ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
+ EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
+ EXPECT_TRUE(VarName.empty());
+ EXPECT_TRUE(ParsedVarResult->IsPseudo);
+
+ VarName = bufferize(SM, "42BadVar");
+ ParsedVarResult = Pattern::parseVariable(VarName, SM);
+ expectDiagnosticError("invalid variable name", ParsedVarResult.takeError());
+
+ VarName = bufferize(SM, "$@");
+ ParsedVarResult = Pattern::parseVariable(VarName, SM);
+ expectDiagnosticError("invalid variable name", ParsedVarResult.takeError());
+
+ VarName = OrigVarName = bufferize(SM, "B@dVar");
+ ParsedVarResult = Pattern::parseVariable(VarName, SM);
+ ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
+ EXPECT_EQ(VarName, OrigVarName.substr(1));
+ EXPECT_EQ(ParsedVarResult->Name, "B");
+ EXPECT_FALSE(ParsedVarResult->IsPseudo);
+
+ VarName = OrigVarName = bufferize(SM, "B$dVar");
+ ParsedVarResult = Pattern::parseVariable(VarName, SM);
+ ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
+ EXPECT_EQ(VarName, OrigVarName.substr(1));
+ EXPECT_EQ(ParsedVarResult->Name, "B");
+ EXPECT_FALSE(ParsedVarResult->IsPseudo);
+
+ VarName = bufferize(SM, "BadVar+");
+ ParsedVarResult = Pattern::parseVariable(VarName, SM);
+ ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
+ EXPECT_EQ(VarName, "+");
+ EXPECT_EQ(ParsedVarResult->Name, "BadVar");
+ EXPECT_FALSE(ParsedVarResult->IsPseudo);
+
+ VarName = bufferize(SM, "BadVar-");
+ ParsedVarResult = Pattern::parseVariable(VarName, SM);
+ ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
+ EXPECT_EQ(VarName, "-");
+ EXPECT_EQ(ParsedVarResult->Name, "BadVar");
+ EXPECT_FALSE(ParsedVarResult->IsPseudo);
+
+ VarName = bufferize(SM, "BadVar:");
+ ParsedVarResult = Pattern::parseVariable(VarName, SM);
+ ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
+ EXPECT_EQ(VarName, ":");
+ EXPECT_EQ(ParsedVarResult->Name, "BadVar");
+ EXPECT_FALSE(ParsedVarResult->IsPseudo);
+}
+
+static void expectNotFoundError(Error Err) {
+ expectError<NotFoundError>("String not found in input", std::move(Err));
+}
+
+class PatternTester {
+private:
+ size_t LineNumber = 1;
+ SourceMgr SM;
+ FileCheckRequest Req;
+ FileCheckPatternContext Context;
+ Pattern P{Check::CheckPlain, &Context, LineNumber};
+
+public:
+ PatternTester() {
+ std::vector<StringRef> GlobalDefines = {"#FOO=42", "BAR=BAZ", "#add=7"};
+ // An ASSERT_FALSE would make more sense but cannot be used in a
+ // constructor.
+ EXPECT_THAT_ERROR(Context.defineCmdlineVariables(GlobalDefines, SM),
+ Succeeded());
+ Context.createLineVariable();
+ // Call parsePattern to have @LINE defined.
+ P.parsePattern("N/A", "CHECK", SM, Req);
+ // parsePattern does not expect to be called twice for the same line and
+ // will set FixedStr and RegExStr incorrectly if it is. Therefore prepare
+ // a pattern for a different line.
+ initNextPattern();
+ }
+
+ void initNextPattern() {
+ P = Pattern(Check::CheckPlain, &Context, ++LineNumber);
+ }
+
+ size_t getLineNumber() const { return LineNumber; }
+
+ Expected<std::unique_ptr<Expression>>
+ parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) {
+ StringRef ExprBufferRef = bufferize(SM, Expr);
+ Optional<NumericVariable *> DefinedNumericVariable;
+ return P.parseNumericSubstitutionBlock(
+ ExprBufferRef, DefinedNumericVariable, IsLegacyLineExpr, LineNumber,
+ &Context, SM);
+ }
+
+ bool parsePattern(StringRef Pattern) {
+ StringRef PatBufferRef = bufferize(SM, Pattern);
+ return P.parsePattern(PatBufferRef, "CHECK", SM, Req);
+ }
+
+ Expected<size_t> match(StringRef Buffer) {
+ StringRef BufferRef = bufferize(SM, Buffer);
+ size_t MatchLen;
+ return P.match(BufferRef, MatchLen, SM);
+ }
+
+ void printVariableDefs(FileCheckDiag::MatchType MatchTy,
+ std::vector<FileCheckDiag> &Diags) {
+ P.printVariableDefs(SM, MatchTy, &Diags);
+ }
+};
+
+TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
+ PatternTester Tester;
+
+ // Variable definition.
+
+ expectDiagnosticError("invalid variable name",
+ Tester.parseSubst("%VAR:").takeError());
+
+ expectDiagnosticError("definition of pseudo numeric variable unsupported",
+ Tester.parseSubst("@LINE:").takeError());
+
+ expectDiagnosticError("string variable with name 'BAR' already exists",
+ Tester.parseSubst("BAR:").takeError());
+
+ expectDiagnosticError("unexpected characters after numeric variable name",
+ Tester.parseSubst("VAR GARBAGE:").takeError());
+
+ // Change of format.
+ expectDiagnosticError("format different from previous variable definition",
+ Tester.parseSubst("%X,FOO:").takeError());
+
+ // Invalid format.
+ expectDiagnosticError("invalid matching format specification in expression",
+ Tester.parseSubst("X,VAR1:").takeError());
+ expectDiagnosticError("invalid format specifier in expression",
+ Tester.parseSubst("%F,VAR1:").takeError());
+ expectDiagnosticError("invalid matching format specification in expression",
+ Tester.parseSubst("%X a,VAR1:").takeError());
+
+ // Acceptable variable definition.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR1:"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst(" VAR2:"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3 :"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3: "), Succeeded());
+
+ // Acceptable variable definition with format specifier. Use parsePattern for
+ // variables whose definition needs to be visible for later checks.
+ EXPECT_FALSE(Tester.parsePattern("[[#%u, VAR_UNSIGNED:]]"));
+ EXPECT_FALSE(Tester.parsePattern("[[#%x, VAR_LOWER_HEX:]]"));
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, VAR_UPPER_HEX:"), Succeeded());
+
+ // Acceptable variable definition with precision specifier.
+ EXPECT_FALSE(Tester.parsePattern("[[#%.8X, PADDED_ADDR:]]"));
+ EXPECT_FALSE(Tester.parsePattern("[[#%.8, PADDED_NUM:]]"));
+
+ // Acceptable variable definition from a numeric expression.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded());
+
+ // Numeric expression. Switch to next line to make above valid definition
+ // available in expressions.
+ Tester.initNextPattern();
+
+ // Invalid variable name.
+ expectDiagnosticError("invalid matching constraint or operand format",
+ Tester.parseSubst("%VAR").takeError());
+
+ expectDiagnosticError("invalid pseudo numeric variable '@FOO'",
+ Tester.parseSubst("@FOO").takeError());
+
+ // parsePattern() is used here instead of parseSubst() for the variable to be
+ // recorded in GlobalNumericVariableTable and thus appear defined to
+ // parseNumericVariableUse(). Note that the same pattern object is used for
+ // the parsePattern() and parseSubst() since no initNextPattern() is called,
+ // thus appearing as being on the same line from the pattern's point of view.
+ ASSERT_FALSE(Tester.parsePattern("[[#SAME_LINE_VAR:]]"));
+ expectDiagnosticError("numeric variable 'SAME_LINE_VAR' defined earlier in "
+ "the same CHECK directive",
+ Tester.parseSubst("SAME_LINE_VAR").takeError());
+
+ // Invalid use of variable defined on the same line from an expression not
+ // using any variable defined on the same line.
+ ASSERT_FALSE(Tester.parsePattern("[[#SAME_LINE_EXPR_VAR:@LINE+1]]"));
+ expectDiagnosticError("numeric variable 'SAME_LINE_EXPR_VAR' defined earlier "
+ "in the same CHECK directive",
+ Tester.parseSubst("SAME_LINE_EXPR_VAR").takeError());
+
+ // Valid use of undefined variable which creates the variable and record it
+ // in GlobalNumericVariableTable.
+ ASSERT_THAT_EXPECTED(Tester.parseSubst("UNDEF"), Succeeded());
+ EXPECT_TRUE(Tester.parsePattern("[[UNDEF:.*]]"));
+
+ // Invalid literal.
+ expectDiagnosticError("unsupported operation 'U'",
+ Tester.parseSubst("42U").takeError());
+
+ // Valid empty expression.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst(""), Succeeded());
+
+ // Invalid equality matching constraint with empty expression.
+ expectDiagnosticError("empty numeric expression should not have a constraint",
+ Tester.parseSubst("==").takeError());
+
+ // Valid single operand expression.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("18"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MaxUint64)),
+ Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("0x12"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("-30"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MinInt64)),
+ Succeeded());
+
+ // Valid optional matching constraint.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("==FOO"), Succeeded());
+
+ // Invalid matching constraint.
+ expectDiagnosticError("invalid matching constraint or operand format",
+ Tester.parseSubst("+=FOO").takeError());
+
+ // Invalid format.
+ expectDiagnosticError("invalid matching format specification in expression",
+ Tester.parseSubst("X,FOO:").takeError());
+ expectDiagnosticError("invalid format specifier in expression",
+ Tester.parseSubst("%F,FOO").takeError());
+ expectDiagnosticError("invalid matching format specification in expression",
+ Tester.parseSubst("%X a,FOO").takeError());
+
+ // Valid expression with 2 or more operands.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+3"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+0xC"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO-3+FOO"), Succeeded());
+
+ expectDiagnosticError("unsupported operation '/'",
+ Tester.parseSubst("@LINE/2").takeError());
+
+ expectDiagnosticError("missing operand in expression",
+ Tester.parseSubst("@LINE+").takeError());
+
+ // Errors in RHS operand are bubbled up by parseBinop() to
+ // parseNumericSubstitutionBlock().
+ expectDiagnosticError("invalid operand format",
+ Tester.parseSubst("@LINE+%VAR").takeError());
+
+ // Invalid legacy @LINE expression with non literal rhs.
+ expectDiagnosticError(
+ "invalid operand format",
+ Tester.parseSubst("@LINE+@LINE", /*IsLegacyNumExpr=*/true).takeError());
+
+ // Invalid legacy @LINE expression made of a single literal.
+ expectDiagnosticError(
+ "invalid variable name",
+ Tester.parseSubst("2", /*IsLegacyNumExpr=*/true).takeError());
+
+ // Invalid hex literal in legacy @LINE expression.
+ expectDiagnosticError(
+ "unexpected characters at end of expression 'xC'",
+ Tester.parseSubst("@LINE+0xC", /*LegacyLineExpr=*/true).takeError());
+
+ // Valid expression with format specifier.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%u, FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%d, FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded());
+
+ // Valid expression with precision specifier.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%.8u, FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%.8, FOO"), Succeeded());
+
+ // Valid legacy @LINE expression.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true),
+ Succeeded());
+
+ // Invalid legacy @LINE expression with more than 2 operands.
+ expectDiagnosticError(
+ "unexpected characters at end of expression '+@LINE'",
+ Tester.parseSubst("@LINE+2+@LINE", /*IsLegacyNumExpr=*/true).takeError());
+ expectDiagnosticError(
+ "unexpected characters at end of expression '+2'",
+ Tester.parseSubst("@LINE+2+2", /*IsLegacyNumExpr=*/true).takeError());
+
+ // Valid expression with several variables when their implicit formats do not
+ // conflict.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+VAR_UNSIGNED"), Succeeded());
+
+ // Valid implicit format conflict in presence of explicit formats.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%X,FOO+VAR_LOWER_HEX"), Succeeded());
+
+ // Implicit format conflict.
+ expectDiagnosticError(
+ "implicit format conflict between 'FOO' (%u) and "
+ "'VAR_LOWER_HEX' (%x), need an explicit format specifier",
+ Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError());
+
+ // Simple parenthesized expressions:
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("(1)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("(1+1)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("(1)+1"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("((1)+1)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("((1)+X)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("((X)+Y)"), Succeeded());
+
+ expectDiagnosticError("missing operand in expression",
+ Tester.parseSubst("(").takeError());
+ expectDiagnosticError("missing ')' at end of nested expression",
+ Tester.parseSubst("(1").takeError());
+ expectDiagnosticError("missing operand in expression",
+ Tester.parseSubst("(1+").takeError());
+ expectDiagnosticError("missing ')' at end of nested expression",
+ Tester.parseSubst("(1+1").takeError());
+ expectDiagnosticError("missing ')' at end of nested expression",
+ Tester.parseSubst("((1+2+3").takeError());
+ expectDiagnosticError("missing ')' at end of nested expression",
+ Tester.parseSubst("((1+2)+3").takeError());
+
+ // Test missing operation between operands:
+ expectDiagnosticError("unsupported operation '('",
+ Tester.parseSubst("(1)(2)").takeError());
+ expectDiagnosticError("unsupported operation '('",
+ Tester.parseSubst("2(X)").takeError());
+
+ // Test more closing than opening parentheses. The diagnostic messages are
+ // not ideal, but for now simply check that we reject invalid input.
+ expectDiagnosticError("invalid matching constraint or operand format",
+ Tester.parseSubst(")").takeError());
+ expectDiagnosticError("unsupported operation ')'",
+ Tester.parseSubst("1)").takeError());
+ expectDiagnosticError("unsupported operation ')'",
+ Tester.parseSubst("(1+2))").takeError());
+ expectDiagnosticError("unsupported operation ')'",
+ Tester.parseSubst("(2))").takeError());
+ expectDiagnosticError("unsupported operation ')'",
+ Tester.parseSubst("(1))(").takeError());
+
+ // Valid expression with function call.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO,3)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add (FOO,3)"), Succeeded());
+ // Valid expression with nested function call.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, min(BAR,10))"), Succeeded());
+ // Valid expression with function call taking expression as argument.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, (BAR+10) + 3)"),
+ Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, min (BAR,10) + 3)"),
+ Succeeded());
+ // Valid expression with variable named the same as a function.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add+FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+add"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(add,add)+add"), Succeeded());
+
+ // Malformed call syntax.
+ expectDiagnosticError("missing ')' at end of call expression",
+ Tester.parseSubst("add(FOO,(BAR+7)").takeError());
+ expectDiagnosticError("missing ')' at end of call expression",
+ Tester.parseSubst("add(FOO,min(BAR,7)").takeError());
+ expectDiagnosticError("missing argument",
+ Tester.parseSubst("add(FOO,)").takeError());
+ expectDiagnosticError("missing argument",
+ Tester.parseSubst("add(,FOO)").takeError());
+ expectDiagnosticError("missing argument",
+ Tester.parseSubst("add(FOO,,3)").takeError());
+
+ // Valid call, but to an unknown function.
+ expectDiagnosticError("call to undefined function 'bogus_function'",
+ Tester.parseSubst("bogus_function(FOO,3)").takeError());
+ expectDiagnosticError("call to undefined function '@add'",
+ Tester.parseSubst("@add(2,3)").takeError());
+ expectDiagnosticError("call to undefined function '$add'",
+ Tester.parseSubst("$add(2,3)").takeError());
+ expectDiagnosticError("call to undefined function 'FOO'",
+ Tester.parseSubst("FOO(2,3)").takeError());
+ expectDiagnosticError("call to undefined function 'FOO'",
+ Tester.parseSubst("FOO (2,3)").takeError());
+
+ // Valid call, but with incorrect argument count.
+ expectDiagnosticError("function 'add' takes 2 arguments but 1 given",
+ Tester.parseSubst("add(FOO)").takeError());
+ expectDiagnosticError("function 'add' takes 2 arguments but 3 given",
+ Tester.parseSubst("add(FOO,3,4)").takeError());
+
+ // Valid call, but not part of a valid expression.
+ expectDiagnosticError("unsupported operation 'a'",
+ Tester.parseSubst("2add(FOO,2)").takeError());
+ expectDiagnosticError("unsupported operation 'a'",
+ Tester.parseSubst("FOO add(FOO,2)").takeError());
+ expectDiagnosticError("unsupported operation 'a'",
+ Tester.parseSubst("add(FOO,2)add(FOO,2)").takeError());
+}
+
+TEST_F(FileCheckTest, ParsePattern) {
+ PatternTester Tester;
+
+ // Invalid space in string substitution.
+ EXPECT_TRUE(Tester.parsePattern("[[ BAR]]"));
+
+ // Invalid variable name in string substitution.
+ EXPECT_TRUE(Tester.parsePattern("[[42INVALID]]"));
+
+ // Invalid string variable definition.
+ EXPECT_TRUE(Tester.parsePattern("[[@PAT:]]"));
+ EXPECT_TRUE(Tester.parsePattern("[[PAT+2:]]"));
+
+ // Collision with numeric variable.
+ EXPECT_TRUE(Tester.parsePattern("[[FOO:]]"));
+
+ // Valid use of string variable.
+ EXPECT_FALSE(Tester.parsePattern("[[BAR]]"));
+
+ // Valid string variable definition.
+ EXPECT_FALSE(Tester.parsePattern("[[PAT:[0-9]+]]"));
+
+ // Invalid numeric substitution.
+ EXPECT_TRUE(Tester.parsePattern("[[#42INVALID]]"));
+
+ // Valid numeric substitution.
+ EXPECT_FALSE(Tester.parsePattern("[[#FOO]]"));
+
+ // Valid legacy @LINE expression.
+ EXPECT_FALSE(Tester.parsePattern("[[@LINE+2]]"));
+
+ // Invalid legacy @LINE expression with non decimal literal.
+ EXPECT_TRUE(Tester.parsePattern("[[@LINE+0x3]]"));
+}
+
+TEST_F(FileCheckTest, Match) {
+ PatternTester Tester;
+
+ // Check a substitution error is diagnosed.
+ ASSERT_FALSE(Tester.parsePattern("[[#%u, -1]]"));
+ expectDiagnosticError(
+ "unable to substitute variable or numeric expression: overflow error",
+ Tester.match("").takeError());
+
+ // Check matching an empty expression only matches a number.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#]]"));
+ expectNotFoundError(Tester.match("FAIL").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+
+ // Check matching a definition only matches a number with the right format.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
+ expectNotFoundError(Tester.match("FAIL").takeError());
+ expectNotFoundError(Tester.match("").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#%u,NUMVAR_UNSIGNED:]]");
+ expectNotFoundError(Tester.match("C").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#%x,NUMVAR_LOWER_HEX:]]");
+ expectNotFoundError(Tester.match("g").takeError());
+ expectNotFoundError(Tester.match("C").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("c"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#%X,NUMVAR_UPPER_HEX:]]");
+ expectNotFoundError(Tester.match("H").takeError());
+ expectNotFoundError(Tester.match("b").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("B"), Succeeded());
+
+ // Check matching expressions with no explicit format matches the values in
+ // the right format.
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#NUMVAR_UNSIGNED-5]]");
+ expectNotFoundError(Tester.match("f").takeError());
+ expectNotFoundError(Tester.match("F").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("15"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#NUMVAR_LOWER_HEX+1]]");
+ expectNotFoundError(Tester.match("13").takeError());
+ expectNotFoundError(Tester.match("D").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("d"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#NUMVAR_UPPER_HEX+1]]");
+ expectNotFoundError(Tester.match("12").takeError());
+ expectNotFoundError(Tester.match("c").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("C"), Succeeded());
+
+ // Check matching an undefined variable returns a NotFound error.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("100"));
+ expectNotFoundError(Tester.match("101").takeError());
+
+ // Check matching the defined variable matches the correct number only.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+
+ // Check matching several substitutions does not match them independently.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR]] [[#NUMVAR+2]]"));
+ expectNotFoundError(Tester.match("19 21").takeError());
+ expectNotFoundError(Tester.match("18 21").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("18 20"), Succeeded());
+
+ // Check matching a numeric expression using @LINE after a match failure uses
+ // the correct value for @LINE.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]"));
+ // Ok, @LINE matches the current line number.
+ EXPECT_THAT_EXPECTED(Tester.match(std::to_string(Tester.getLineNumber())),
+ Succeeded());
+ Tester.initNextPattern();
+ // Match with substitution failure.
+ ASSERT_FALSE(Tester.parsePattern("[[#UNKNOWN]]"));
+ expectUndefErrors({"UNKNOWN"}, Tester.match("FOO").takeError());
+ Tester.initNextPattern();
+ // Check that @LINE matches the later (given the calls to initNextPattern())
+ // line number.
+ EXPECT_FALSE(Tester.parsePattern("[[#@LINE]]"));
+ EXPECT_THAT_EXPECTED(Tester.match(std::to_string(Tester.getLineNumber())),
+ Succeeded());
+}
+
+TEST_F(FileCheckTest, MatchParen) {
+ PatternTester Tester;
+ // Check simple parenthesized expressions
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
+ expectNotFoundError(Tester.match("FAIL").takeError());
+ expectNotFoundError(Tester.match("").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR + (2 + 2)]]"));
+ expectNotFoundError(Tester.match("21").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("22"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR + (2)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(NUMVAR)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("36"), Succeeded());
+
+ // Check nested parenthesized expressions:
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2+(2))]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("22"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2+(NUMVAR))]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("38"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+((((NUMVAR))))]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("36"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+((((NUMVAR)))-1)-1]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("34"), Succeeded());
+
+ // Parentheses can also be the first character after the '#':
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#(NUMVAR)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#(NUMVAR+2)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
+}
+
+TEST_F(FileCheckTest, MatchBuiltinFunctions) {
+ PatternTester Tester;
+ // Esnure #NUMVAR has the expected value.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
+ expectNotFoundError(Tester.match("FAIL").takeError());
+ expectNotFoundError(Tester.match("").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+
+ // Check each builtin function generates the expected result.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#add(NUMVAR,13)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("31"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#div(NUMVAR,3)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("6"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,5)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,99)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("99"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,5)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("5"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,99)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#mul(NUMVAR,3)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("54"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#sub(NUMVAR,7)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("11"), Succeeded());
+
+ // Check nested function calls.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#add(min(7,2),max(4,10))]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("12"), Succeeded());
+
+ // Check function call that uses a variable of the same name.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#add(add,add)+min (add,3)+add]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("24"), Succeeded());
+}
+
+TEST_F(FileCheckTest, Substitution) {
+ SourceMgr SM;
+ FileCheckPatternContext Context;
+ EXPECT_THAT_ERROR(Context.defineCmdlineVariables({"FOO=BAR"}, SM),
+ Succeeded());
+
+ // Substitution of an undefined string variable fails and error holds that
+ // variable's name.
+ StringSubstitution StringSubstitution(&Context, "VAR404", 42);
+ Expected<std::string> SubstValue = StringSubstitution.getResult();
+ expectUndefErrors({"VAR404"}, SubstValue.takeError());
+
+ // Numeric substitution blocks constituted of defined numeric variables are
+ // substituted for the variable's value.
+ NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned),
+ 1);
+ NVar.setValue(ExpressionValue(10u));
+ auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar);
+ auto ExpressionN = std::make_unique<Expression>(
+ std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper));
+ NumericSubstitution SubstitutionN(&Context, "N", std::move(ExpressionN),
+ /*InsertIdx=*/30);
+ SubstValue = SubstitutionN.getResult();
+ ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
+ EXPECT_EQ("A", *SubstValue);
+
+ // Substitution of an undefined numeric variable fails, error holds name of
+ // undefined variable.
+ NVar.clearValue();
+ SubstValue = SubstitutionN.getResult();
+ expectUndefErrors({"N"}, SubstValue.takeError());
+
+ // Substitution of a defined string variable returns the right value.
+ Pattern P(Check::CheckPlain, &Context, 1);
+ StringSubstitution = llvm::StringSubstitution(&Context, "FOO", 42);
+ SubstValue = StringSubstitution.getResult();
+ ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
+ EXPECT_EQ("BAR", *SubstValue);
+}
+
+TEST_F(FileCheckTest, FileCheckContext) {
+ FileCheckPatternContext Cxt;
+ SourceMgr SM;
+
+ // No definition.
+ EXPECT_THAT_ERROR(Cxt.defineCmdlineVariables({}, SM), Succeeded());
+
+ // Missing equal sign.
+ expectDiagnosticError("missing equal sign in global definition",
+ Cxt.defineCmdlineVariables({"LocalVar"}, SM));
+ expectDiagnosticError("missing equal sign in global definition",
+ Cxt.defineCmdlineVariables({"#LocalNumVar"}, SM));
+
+ // Empty variable name.
+ expectDiagnosticError("empty variable name",
+ Cxt.defineCmdlineVariables({"=18"}, SM));
+ expectDiagnosticError("empty variable name",
+ Cxt.defineCmdlineVariables({"#=18"}, SM));
+
+ // Invalid variable name.
+ expectDiagnosticError("invalid variable name",
+ Cxt.defineCmdlineVariables({"18LocalVar=18"}, SM));
+ expectDiagnosticError("invalid variable name",
+ Cxt.defineCmdlineVariables({"#18LocalNumVar=18"}, SM));
+
+ // Name conflict between pattern and numeric variable.
+ expectDiagnosticError(
+ "string variable with name 'LocalVar' already exists",
+ Cxt.defineCmdlineVariables({"LocalVar=18", "#LocalVar=36"}, SM));
+ Cxt = FileCheckPatternContext();
+ expectDiagnosticError(
+ "numeric variable with name 'LocalNumVar' already exists",
+ Cxt.defineCmdlineVariables({"#LocalNumVar=18", "LocalNumVar=36"}, SM));
+ Cxt = FileCheckPatternContext();
+
+ // Invalid numeric value for numeric variable.
+ expectUndefErrors({"x"}, Cxt.defineCmdlineVariables({"#LocalNumVar=x"}, SM));
+
+ // Define local variables from command-line.
+ std::vector<StringRef> GlobalDefines;
+ // Clear local variables to remove dummy numeric variable x that
+ // parseNumericSubstitutionBlock would have created and stored in
+ // GlobalNumericVariableTable.
+ Cxt.clearLocalVars();
+ GlobalDefines.emplace_back("LocalVar=FOO");
+ GlobalDefines.emplace_back("EmptyVar=");
+ GlobalDefines.emplace_back("#LocalNumVar1=18");
+ GlobalDefines.emplace_back("#%x,LocalNumVar2=LocalNumVar1+2");
+ GlobalDefines.emplace_back("#LocalNumVar3=0xc");
+ ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
+
+ // Create @LINE pseudo numeric variable and check it is present by matching
+ // it.
+ size_t LineNumber = 1;
+ Pattern P(Check::CheckPlain, &Cxt, LineNumber);
+ FileCheckRequest Req;
+ Cxt.createLineVariable();
+ ASSERT_FALSE(P.parsePattern("[[@LINE]]", "CHECK", SM, Req));
+ size_t MatchLen;
+ ASSERT_THAT_EXPECTED(P.match("1", MatchLen, SM), Succeeded());
+
+#ifndef NDEBUG
+ // Recreating @LINE pseudo numeric variable fails.
+ EXPECT_DEATH(Cxt.createLineVariable(),
+ "@LINE pseudo numeric variable already created");
+#endif
+
+ // Check defined variables are present and undefined ones are absent.
+ StringRef LocalVarStr = "LocalVar";
+ StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
+ StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
+ StringRef LocalNumVar3Ref = bufferize(SM, "LocalNumVar3");
+ StringRef EmptyVarStr = "EmptyVar";
+ StringRef UnknownVarStr = "UnknownVar";
+ Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
+ P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
+ Optional<NumericVariable *> DefinedNumericVariable;
+ Expected<std::unique_ptr<Expression>> ExpressionPointer =
+ P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
+ /*IsLegacyLineExpr=*/false, LineNumber,
+ &Cxt, SM);
+ ASSERT_THAT_EXPECTED(LocalVar, Succeeded());
+ EXPECT_EQ(*LocalVar, "FOO");
+ Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
+ Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ Expected<ExpressionValue> ExpressionVal =
+ (*ExpressionPointer)->getAST()->eval();
+ ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 18);
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
+ LocalNumVar2Ref, DefinedNumericVariable,
+ /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
+ ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 20);
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
+ LocalNumVar3Ref, DefinedNumericVariable,
+ /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
+ ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 12);
+ ASSERT_THAT_EXPECTED(EmptyVar, Succeeded());
+ EXPECT_EQ(*EmptyVar, "");
+ expectUndefErrors({std::string(UnknownVarStr)}, UnknownVar.takeError());
+
+ // Clear local variables and check they become absent.
+ Cxt.clearLocalVars();
+ LocalVar = Cxt.getPatternVarValue(LocalVarStr);
+ expectUndefErrors({std::string(LocalVarStr)}, LocalVar.takeError());
+ // Check a numeric expression's evaluation fails if called after clearing of
+ // local variables, if it was created before. This is important because local
+ // variable clearing due to --enable-var-scope happens after numeric
+ // expressions are linked to the numeric variables they use.
+ expectUndefErrors({"LocalNumVar3"},
+ (*ExpressionPointer)->getAST()->eval().takeError());
+ P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
+ LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
+ LineNumber, &Cxt, SM);
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
+ expectUndefErrors({"LocalNumVar1"}, ExpressionVal.takeError());
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
+ LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
+ LineNumber, &Cxt, SM);
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
+ expectUndefErrors({"LocalNumVar2"}, ExpressionVal.takeError());
+ EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
+ expectUndefErrors({"EmptyVar"}, EmptyVar.takeError());
+ // Clear again because parseNumericSubstitutionBlock would have created a
+ // dummy variable and stored it in GlobalNumericVariableTable.
+ Cxt.clearLocalVars();
+
+ // Redefine global variables and check variables are defined again.
+ GlobalDefines.emplace_back("$GlobalVar=BAR");
+ GlobalDefines.emplace_back("#$GlobalNumVar=36");
+ ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
+ StringRef GlobalVarStr = "$GlobalVar";
+ StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");
+ Expected<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
+ ASSERT_THAT_EXPECTED(GlobalVar, Succeeded());
+ EXPECT_EQ(*GlobalVar, "BAR");
+ P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
+ GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
+ LineNumber, &Cxt, SM);
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
+ ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36);
+
+ // Clear local variables and check global variables remain defined.
+ Cxt.clearLocalVars();
+ EXPECT_THAT_EXPECTED(Cxt.getPatternVarValue(GlobalVarStr), Succeeded());
+ P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
+ GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
+ LineNumber, &Cxt, SM);
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
+ ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36);
+}
+
+TEST_F(FileCheckTest, CapturedVarDiags) {
+ PatternTester Tester;
+ ASSERT_FALSE(Tester.parsePattern("[[STRVAR:[a-z]+]] [[#NUMVAR:@LINE]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("foobar 2"), Succeeded());
+ std::vector<FileCheckDiag> Diags;
+ Tester.printVariableDefs(FileCheckDiag::MatchFoundAndExpected, Diags);
+ EXPECT_EQ(Diags.size(), 2ul);
+ for (FileCheckDiag Diag : Diags) {
+ EXPECT_EQ(Diag.CheckTy, Check::CheckPlain);
+ EXPECT_EQ(Diag.MatchTy, FileCheckDiag::MatchFoundAndExpected);
+ EXPECT_EQ(Diag.InputStartLine, 1u);
+ EXPECT_EQ(Diag.InputEndLine, 1u);
+ }
+ EXPECT_EQ(Diags[0].InputStartCol, 1u);
+ EXPECT_EQ(Diags[0].InputEndCol, 7u);
+ EXPECT_EQ(Diags[1].InputStartCol, 8u);
+ EXPECT_EQ(Diags[1].InputEndCol, 9u);
+ EXPECT_EQ(Diags[0].Note, "captured var \"STRVAR\"");
+ EXPECT_EQ(Diags[1].Note, "captured var \"NUMVAR\"");
+}
+} // namespace