diff options
Diffstat (limited to 'llvm/unittests/Support/FileCheckTest.cpp')
-rw-r--r-- | llvm/unittests/Support/FileCheckTest.cpp | 1724 |
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 |