| //===- llvm/unittest/FileCheck/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/FileCheck/FileCheck.h" |
| #include "../lib/FileCheck/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::tuple<ExpressionFormat::Kind, unsigned, bool>> { |
| bool AlternateForm; |
| 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, AlternateForm) = GetParam(); |
| AllowHex = Kind == ExpressionFormat::Kind::HexLower || |
| Kind == ExpressionFormat::Kind::HexUpper; |
| AllowUpperHex = Kind == ExpressionFormat::Kind::HexUpper; |
| Signed = Kind == ExpressionFormat::Kind::Signed; |
| Format = ExpressionFormat(Kind, Precision, AlternateForm); |
| |
| 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 { |
| ASSERT_TRUE(TrailExtendTo == 0 || AllowHex); |
| SmallVector<StringRef, 4> Matches; |
| std::string ExtendedInput = Input.str(); |
| size_t PrefixSize = AlternateForm ? 2 : 0; |
| if (TrailExtendTo > Input.size() - PrefixSize) { |
| size_t ExtensionSize = PrefixSize + TrailExtendTo - Input.size(); |
| ExtendedInput.append(ExtensionSize, Input[PrefixSize]); |
| } |
| 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)); |
| } |
| |
| std::string addBasePrefix(StringRef Num) const { |
| StringRef Prefix = AlternateForm ? "0x" : ""; |
| return (Twine(Prefix) + Twine(Num)).str(); |
| } |
| |
| void checkPerCharWildcardRegexMatchFailure(StringRef Chars) const { |
| for (auto C : Chars) { |
| std::string Str = addBasePrefix(StringRef(&C, 1)); |
| EXPECT_FALSE(WildcardRegex.match(Str)); |
| } |
| } |
| |
| 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 ErrorStr = "unable to represent numeric value") { |
| Expected<ExpressionValue> ResultValue = getValueFromStringReprFailure(Str); |
| expectDiagnosticError(ErrorStr, 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, matches several of them and match 0x prefix |
| // if and only if AlternateForm is true. |
| StringRef LongNumber = "12345678901234567890"; |
| StringRef PrefixedLongNumber = "0x12345678901234567890"; |
| if (AlternateForm) { |
| checkWildcardRegexMatch(PrefixedLongNumber); |
| checkWildcardRegexMatchFailure(LongNumber); |
| } else { |
| checkWildcardRegexMatch(LongNumber); |
| checkWildcardRegexMatchFailure(PrefixedLongNumber); |
| } |
| |
| // Matches negative digits. |
| LongNumber = "-12345678901234567890"; |
| if (Signed) |
| checkWildcardRegexMatch(LongNumber); |
| else |
| checkWildcardRegexMatchFailure(LongNumber); |
| |
| // Check non digits or digits with wrong casing are not matched. |
| std::string LongNumberStr; |
| if (AllowHex) { |
| LongNumberStr = addBasePrefix(AcceptedHexOnlyDigits); |
| checkWildcardRegexMatch(LongNumberStr, 16); |
| checkPerCharWildcardRegexMatchFailure(RefusedHexOnlyDigits); |
| } |
| checkPerCharWildcardRegexMatchFailure(FirstInvalidCharDigits); |
| |
| // Check leading zeros are only accepted if number of digits is less than the |
| // precision. |
| LongNumber = "01234567890123456789"; |
| if (Precision) { |
| LongNumberStr = addBasePrefix(LongNumber.take_front(Precision)); |
| checkWildcardRegexMatch(LongNumberStr); |
| LongNumberStr = addBasePrefix(LongNumber.take_front(Precision - 1)); |
| checkWildcardRegexMatchFailure(LongNumberStr); |
| if (Precision < LongNumber.size()) { |
| LongNumberStr = addBasePrefix(LongNumber.take_front(Precision + 1)); |
| checkWildcardRegexMatchFailure(LongNumberStr); |
| } |
| } else { |
| LongNumberStr = addBasePrefix(LongNumber); |
| checkWildcardRegexMatch(LongNumberStr); |
| } |
| } |
| |
| TEST_P(ExpressionFormatParameterisedFixture, FormatGetMatchingString) { |
| checkMatchingString(0, addBasePrefix(padWithLeadingZeros("0"))); |
| checkMatchingString(9, addBasePrefix(padWithLeadingZeros("9"))); |
| |
| if (Signed) { |
| checkMatchingString(-5, padWithLeadingZeros("-5")); |
| checkMatchingStringFailure(MaxUint64); |
| checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str)); |
| checkMatchingString(MinInt64, padWithLeadingZeros(MinInt64Str)); |
| } else { |
| checkMatchingStringFailure(-5); |
| checkMatchingString(MaxUint64, |
| addBasePrefix(padWithLeadingZeros(MaxUint64Str))); |
| checkMatchingString(MaxInt64, |
| addBasePrefix(padWithLeadingZeros(MaxInt64Str))); |
| checkMatchingStringFailure(MinInt64); |
| } |
| |
| checkMatchingString(10, addBasePrefix(padWithLeadingZeros(TenStr))); |
| checkMatchingString(15, addBasePrefix(padWithLeadingZeros(FifteenStr))); |
| } |
| |
| TEST_P(ExpressionFormatParameterisedFixture, FormatValueFromStringRepr) { |
| checkValueFromStringRepr(addBasePrefix("0"), 0); |
| checkValueFromStringRepr(addBasePrefix("9"), 9); |
| |
| if (Signed) { |
| checkValueFromStringRepr("-5", -5); |
| checkValueFromStringReprFailure(MaxUint64Str); |
| } else { |
| checkValueFromStringReprFailure("-" + addBasePrefix("5")); |
| checkValueFromStringRepr(addBasePrefix(MaxUint64Str), MaxUint64); |
| } |
| |
| checkValueFromStringRepr(addBasePrefix(TenStr), 10); |
| checkValueFromStringRepr(addBasePrefix(FifteenStr), 15); |
| |
| // Wrong casing is not tested because valueFromStringRepr() relies on |
| // StringRef's getAsInteger() which does not allow to restrict casing. |
| checkValueFromStringReprFailure(addBasePrefix("G")); |
| |
| if (AlternateForm) |
| checkValueFromStringReprFailure("9", "missing alternate form prefix"); |
| } |
| |
| TEST_P(ExpressionFormatParameterisedFixture, FormatBoolOperator) { |
| EXPECT_TRUE(bool(Format)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture, |
| ::testing::Values( |
| std::make_tuple(ExpressionFormat::Kind::Unsigned, 0, false), |
| std::make_tuple(ExpressionFormat::Kind::Signed, 0, false), |
| std::make_tuple(ExpressionFormat::Kind::HexLower, 0, false), |
| std::make_tuple(ExpressionFormat::Kind::HexLower, 0, true), |
| std::make_tuple(ExpressionFormat::Kind::HexUpper, 0, false), |
| std::make_tuple(ExpressionFormat::Kind::HexUpper, 0, true), |
| |
| std::make_tuple(ExpressionFormat::Kind::Unsigned, 1, false), |
| std::make_tuple(ExpressionFormat::Kind::Signed, 1, false), |
| std::make_tuple(ExpressionFormat::Kind::HexLower, 1, false), |
| std::make_tuple(ExpressionFormat::Kind::HexLower, 1, true), |
| std::make_tuple(ExpressionFormat::Kind::HexUpper, 1, false), |
| std::make_tuple(ExpressionFormat::Kind::HexUpper, 1, true), |
| |
| std::make_tuple(ExpressionFormat::Kind::Unsigned, 16, false), |
| std::make_tuple(ExpressionFormat::Kind::Signed, 16, false), |
| std::make_tuple(ExpressionFormat::Kind::HexLower, 16, false), |
| std::make_tuple(ExpressionFormat::Kind::HexLower, 16, true), |
| std::make_tuple(ExpressionFormat::Kind::HexUpper, 16, false), |
| std::make_tuple(ExpressionFormat::Kind::HexUpper, 16, true), |
| |
| std::make_tuple(ExpressionFormat::Kind::Unsigned, 20, false), |
| std::make_tuple(ExpressionFormat::Kind::Signed, 20, false))); |
| |
| 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); |
| Pattern::MatchResult Res = P.match(BufferRef, SM); |
| if (Res.TheError) |
| return std::move(Res.TheError); |
| return Res.TheMatch->Pos; |
| } |
| |
| 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 in alternate form. |
| EXPECT_THAT_EXPECTED(Tester.parseSubst("%#x, PREFIXED_ADDR:"), Succeeded()); |
| EXPECT_THAT_EXPECTED(Tester.parseSubst("%#X, PREFIXED_ADDR:"), Succeeded()); |
| |
| // Acceptable variable definition in alternate form. |
| expectDiagnosticError("alternate form only supported for hex values", |
| Tester.parseSubst("%#u, PREFIXED_UNSI:").takeError()); |
| expectDiagnosticError("alternate form only supported for hex values", |
| Tester.parseSubst("%#d, PREFIXED_UNSI:").takeError()); |
| |
| // 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:]]")); |
| |
| // Invalid use of string variable. |
| EXPECT_TRUE(Tester.parsePattern("[[FOO-BAR]]")); |
| |
| // 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("[[#UNKNOWN1+UNKNOWN2]]")); |
| expectSameErrors<ErrorDiagnostic>( |
| {"undefined variable: UNKNOWN1", "undefined variable: UNKNOWN2"}, |
| 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)); |
| Pattern::MatchResult Res = P.match("1", SM); |
| ASSERT_THAT_ERROR(std::move(Res.TheError), 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 |