//===- 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 #include using namespace llvm; namespace { class FileCheckTest : public ::testing::Test {}; static StringRef bufferize(SourceMgr &SM, StringRef Str) { std::unique_ptr 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 &Set) { bool First = true; std::string Str; for (StringRef S : Set) { Str += Twine(First ? "{" + S : ", " + S).str(); First = false; } Str += '}'; return Str; } template static void expectSameErrors(std::unordered_set 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 static void expectError(StringRef ExpectedMsg, Error Err) { expectSameErrors({ExpectedMsg.str()}, std::move(Err)); } static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) { expectError(ExpectedMsg, std::move(Err)); } constexpr uint64_t MaxUint64 = std::numeric_limits::max(); constexpr int64_t MaxInt64 = std::numeric_limits::max(); constexpr int64_t MinInt64 = std::numeric_limits::min(); constexpr uint64_t AbsoluteMinInt64 = static_cast(-(MinInt64 + 1)) + 1; constexpr uint64_t AbsoluteMaxInt64 = static_cast(MaxInt64); struct ExpressionFormatParameterisedFixture : public ::testing::TestWithParam< std::pair> { 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 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 void checkMatchingString(T Val, StringRef ExpectedStr) { Expected MatchingString = Format.getMatchingString(ExpressionValue(Val)); ASSERT_THAT_EXPECTED(MatchingString, Succeeded()) << "No matching string for " << Val; EXPECT_EQ(*MatchingString, ExpectedStr); } template void checkMatchingStringFailure(T Val) { Expected MatchingString = Format.getMatchingString(ExpressionValue(Val)); // Error message tested in ExpressionValue unit tests. EXPECT_THAT_EXPECTED(MatchingString, Failed()); } Expected getValueFromStringReprFailure(StringRef Str) { StringRef BufferizedStr = bufferize(SM, Str); return Format.valueFromStringRepr(BufferizedStr, SM); } template void checkValueFromStringRepr(StringRef Str, T ExpectedVal) { Expected 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(ExpectedVal)); else EXPECT_EQ(cantFail(ResultValue->getUnsignedValue()), static_cast(ExpectedVal)); } void checkValueFromStringReprFailure(StringRef Str) { StringRef OverflowErrorStr = "unable to represent numeric value"; Expected ResultValue = getValueFromStringReprFailure(Str); expectDiagnosticError(OverflowErrorStr, ResultValue.takeError()); } }; TEST_P(ExpressionFormatParameterisedFixture, FormatGetWildcardRegex) { // Wildcard regex is valid. Expected 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("trying to match value with invalid format", NoFormat.getWildcardRegex().takeError()); expectError( "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 static Expected doValueOperation(binop_eval_t Operation, T1 LeftValue, T2 RightValue) { ExpressionValue LeftOperand(LeftValue); ExpressionValue RightOperand(RightValue); return Operation(LeftOperand, RightOperand); } template static void expectValueEqual(ExpressionValue ActualValue, T ExpectedValue) { EXPECT_EQ(ExpectedValue < 0, ActualValue.isNegative()); if (ExpectedValue < 0) { Expected SignedActualValue = ActualValue.getSignedValue(); ASSERT_THAT_EXPECTED(SignedActualValue, Succeeded()); EXPECT_EQ(*SignedActualValue, static_cast(ExpectedValue)); } else { Expected UnsignedActualValue = ActualValue.getUnsignedValue(); ASSERT_THAT_EXPECTED(UnsignedActualValue, Succeeded()); EXPECT_EQ(*UnsignedActualValue, static_cast(ExpectedValue)); } } template static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue, T2 RightValue, TR ResultValue) { Expected OperationResult = doValueOperation(Operation, LeftValue, RightValue); ASSERT_THAT_EXPECTED(OperationResult, Succeeded()); expectValueEqual(*OperationResult, ResultValue); } template static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue, T2 RightValue) { expectError( "overflow error", doValueOperation(Operation, LeftValue, RightValue).takeError()); } TEST_F(FileCheckTest, ExpressionValueGetUnsigned) { // Test positive value. Expected 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( "overflow error", ExpressionValue(-1).getUnsignedValue().takeError()); // Test failure with min negative value. expectError( "overflow error", ExpressionValue(MinInt64).getUnsignedValue().takeError()); } TEST_F(FileCheckTest, ExpressionValueGetSigned) { // Test positive value. Expected 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( "overflow error", ExpressionValue(static_cast(MaxInt64) + 1) .getSignedValue() .takeError()); // Test failure with max uint64_t. expectError( "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(-(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(-(MinInt64 + 10)) + 11); // Test both positive values with result < -(max int64_t) expectOperationValueResult(operator-, 10, static_cast(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 Value = Ten.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); EXPECT_EQ(10, cantFail(Value->getSignedValue())); Expected 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 Ten = std::make_unique(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 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 ImplicitFormat = FooVarUse.getImplicitFormat(SM); ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); EXPECT_FALSE(FooVar.getValue()); Expected EvalResult = FooVarUse.eval(); expectUndefErrors({"FOO"}, EvalResult.takeError()); // Defined variable without string: only getValue and eval return value set. FooVar.setValue(ExpressionValue(42u)); Optional 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 FooVarUse = std::make_unique(FooStr, &FooVar); StringRef BarStr = ExprStr.take_back(3); NumericVariable BarVar(BarStr, ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2); BarVar.setValue(ExpressionValue(18u)); std::unique_ptr BarVarUse = std::make_unique(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 Value = Binop.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); EXPECT_EQ(60, cantFail(Value->getSignedValue())); Expected 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(FooStr, &FooVar); std::unique_ptr Eighteen = std::make_unique(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(FooStr, &FooVar); Eighteen = std::make_unique(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(FooStr, &FooVar); std::unique_ptr BazVarUse = std::make_unique(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(FooStr, &FooVar); BazVarUse = std::make_unique(BazStr, &BazVar); std::unique_ptr FooVarUse2 = std::make_unique(FooStr2, &FooVar); NumericVariable QuuxVar( QuuxStr, ExpressionFormat(ExpressionFormat::Kind::HexLower), 4); std::unique_ptr QuuxVarUse = std::make_unique(QuuxStr, &QuuxVar); std::unique_ptr Binop1 = std::make_unique( ExprStr.take_front(9), doAdd, std::move(FooVarUse), std::move(BazVarUse)); std::unique_ptr Binop2 = std::make_unique( ExprStr.take_back(10), doAdd, std::move(FooVarUse2), std::move(QuuxVarUse)); std::unique_ptr OuterBinop = std::make_unique(ExprStr, doAdd, std::move(Binop1), std::move(Binop2)); ImplicitFormat = OuterBinop->getImplicitFormat(SM); expectSameErrors( {("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 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("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 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> parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) { StringRef ExprBufferRef = bufferize(SM, Expr); Optional 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 match(StringRef Buffer) { StringRef BufferRef = bufferize(SM, Buffer); size_t MatchLen; return P.match(BufferRef, MatchLen, SM); } void printVariableDefs(FileCheckDiag::MatchType MatchTy, std::vector &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 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("N", &NVar); auto ExpressionN = std::make_unique( 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 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 LocalVar = Cxt.getPatternVarValue(LocalVarStr); P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber); Optional DefinedNumericVariable; Expected> ExpressionPointer = P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); ASSERT_THAT_EXPECTED(LocalVar, Succeeded()); EXPECT_EQ(*LocalVar, "FOO"); Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); Expected 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 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 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