mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-05-17 04:25:58 +00:00

A more general name might be match-time error propagation. That is, it's conceivable we'll one day have non-numeric errors that require the handling fixed by this patch. Without this patch, FileCheck behaves as follows: ``` $ cat check CHECK-NOT: [[#0x8000000000000000+0x8000000000000000]] $ FileCheck -vv -dump-input=never check < input check:1:54: remark: implicit EOF: expected string found in input CHECK-NOT: [[#0x8000000000000000+0x8000000000000000]] ^ <stdin>:2:1: note: found here ^ check:1:15: error: unable to substitute variable or numeric expression: overflow error CHECK-NOT: [[#0x8000000000000000+0x8000000000000000]] ^ $ echo $? 0 ``` Notice that the exit status is 0 even though there's an error. Moreover, FileCheck doesn't print the error diagnostic unless both `-dump-input=never` and `-vv` are specified. The same problem occurs when `CHECK-NOT` does have a match but a capture fails due to overflow: exit status is 0, and no diagnostic is printed unless both `-dump-input=never` and `-vv` are specified. The usefulness of capturing from `CHECK-NOT` is questionable, but this case should certainly produce an error. With this patch, FileCheck always includes the error diagnostic and has non-zero exit status for the above examples. It's conceivable that this change will cause some existing tests to fail, but my assumption is that they should fail. Moreover, with nearly every project enabled, this patch didn't produce additional `check-all` failures for me. This patch also extends input dumps to include such numeric error diagnostics for both expected and excluded patterns. As noted in fixmes in some of the tests added by this patch, this patch worsens an existing issue with redundant diagnostics. I'll fix that bug in a subsequent patch. Reviewed By: thopre, jhenderson Differential Revision: https://reviews.llvm.org/D98086
1776 lines
74 KiB
C++
1776 lines
74 KiB
C++
//===- 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 checkWildcardRegexCharMatchFailure(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);
|
|
checkWildcardRegexCharMatchFailure(RefusedHexOnlyDigits);
|
|
}
|
|
checkWildcardRegexCharMatchFailure(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_CASE_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:]]"));
|
|
|
|
// Valid use of string variable.
|
|
EXPECT_FALSE(Tester.parsePattern("[[BAR]]"));
|
|
|
|
// Valid string variable definition.
|
|
EXPECT_FALSE(Tester.parsePattern("[[PAT:[0-9]+]]"));
|
|
|
|
// Invalid numeric substitution.
|
|
EXPECT_TRUE(Tester.parsePattern("[[#42INVALID]]"));
|
|
|
|
// Valid numeric substitution.
|
|
EXPECT_FALSE(Tester.parsePattern("[[#FOO]]"));
|
|
|
|
// Valid legacy @LINE expression.
|
|
EXPECT_FALSE(Tester.parsePattern("[[@LINE+2]]"));
|
|
|
|
// Invalid legacy @LINE expression with non decimal literal.
|
|
EXPECT_TRUE(Tester.parsePattern("[[@LINE+0x3]]"));
|
|
}
|
|
|
|
TEST_F(FileCheckTest, Match) {
|
|
PatternTester Tester;
|
|
|
|
// Check a substitution error is diagnosed.
|
|
ASSERT_FALSE(Tester.parsePattern("[[#%u, -1]]"));
|
|
expectDiagnosticError(
|
|
"unable to substitute variable or numeric expression: overflow error",
|
|
Tester.match("").takeError());
|
|
|
|
// Check matching an empty expression only matches a number.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#]]"));
|
|
expectNotFoundError(Tester.match("FAIL").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
|
|
|
|
// Check matching a definition only matches a number with the right format.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
|
|
expectNotFoundError(Tester.match("FAIL").takeError());
|
|
expectNotFoundError(Tester.match("").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
|
|
Tester.initNextPattern();
|
|
Tester.parsePattern("[[#%u,NUMVAR_UNSIGNED:]]");
|
|
expectNotFoundError(Tester.match("C").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
|
|
Tester.initNextPattern();
|
|
Tester.parsePattern("[[#%x,NUMVAR_LOWER_HEX:]]");
|
|
expectNotFoundError(Tester.match("g").takeError());
|
|
expectNotFoundError(Tester.match("C").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("c"), Succeeded());
|
|
Tester.initNextPattern();
|
|
Tester.parsePattern("[[#%X,NUMVAR_UPPER_HEX:]]");
|
|
expectNotFoundError(Tester.match("H").takeError());
|
|
expectNotFoundError(Tester.match("b").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("B"), Succeeded());
|
|
|
|
// Check matching expressions with no explicit format matches the values in
|
|
// the right format.
|
|
Tester.initNextPattern();
|
|
Tester.parsePattern("[[#NUMVAR_UNSIGNED-5]]");
|
|
expectNotFoundError(Tester.match("f").takeError());
|
|
expectNotFoundError(Tester.match("F").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("15"), Succeeded());
|
|
Tester.initNextPattern();
|
|
Tester.parsePattern("[[#NUMVAR_LOWER_HEX+1]]");
|
|
expectNotFoundError(Tester.match("13").takeError());
|
|
expectNotFoundError(Tester.match("D").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("d"), Succeeded());
|
|
Tester.initNextPattern();
|
|
Tester.parsePattern("[[#NUMVAR_UPPER_HEX+1]]");
|
|
expectNotFoundError(Tester.match("12").takeError());
|
|
expectNotFoundError(Tester.match("c").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("C"), Succeeded());
|
|
|
|
// Check matching an undefined variable returns a NotFound error.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("100"));
|
|
expectNotFoundError(Tester.match("101").takeError());
|
|
|
|
// Check matching the defined variable matches the correct number only.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
|
|
|
|
// Check matching several substitutions does not match them independently.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR]] [[#NUMVAR+2]]"));
|
|
expectNotFoundError(Tester.match("19 21").takeError());
|
|
expectNotFoundError(Tester.match("18 21").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("18 20"), Succeeded());
|
|
|
|
// Check matching a numeric expression using @LINE after a match failure uses
|
|
// the correct value for @LINE.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]"));
|
|
// Ok, @LINE matches the current line number.
|
|
EXPECT_THAT_EXPECTED(Tester.match(std::to_string(Tester.getLineNumber())),
|
|
Succeeded());
|
|
Tester.initNextPattern();
|
|
// Match with substitution failure.
|
|
ASSERT_FALSE(Tester.parsePattern("[[#UNKNOWN]]"));
|
|
expectUndefErrors({"UNKNOWN"}, Tester.match("FOO").takeError());
|
|
Tester.initNextPattern();
|
|
// Check that @LINE matches the later (given the calls to initNextPattern())
|
|
// line number.
|
|
EXPECT_FALSE(Tester.parsePattern("[[#@LINE]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match(std::to_string(Tester.getLineNumber())),
|
|
Succeeded());
|
|
}
|
|
|
|
TEST_F(FileCheckTest, MatchParen) {
|
|
PatternTester Tester;
|
|
// Check simple parenthesized expressions
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
|
|
expectNotFoundError(Tester.match("FAIL").takeError());
|
|
expectNotFoundError(Tester.match("").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
|
|
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR + (2 + 2)]]"));
|
|
expectNotFoundError(Tester.match("21").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("22"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR + (2)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(NUMVAR)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("36"), Succeeded());
|
|
|
|
// Check nested parenthesized expressions:
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2+(2))]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("22"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2+(NUMVAR))]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("38"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+((((NUMVAR))))]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("36"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+((((NUMVAR)))-1)-1]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("34"), Succeeded());
|
|
|
|
// Parentheses can also be the first character after the '#':
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#(NUMVAR)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#(NUMVAR+2)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
|
|
}
|
|
|
|
TEST_F(FileCheckTest, MatchBuiltinFunctions) {
|
|
PatternTester Tester;
|
|
// Esnure #NUMVAR has the expected value.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
|
|
expectNotFoundError(Tester.match("FAIL").takeError());
|
|
expectNotFoundError(Tester.match("").takeError());
|
|
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
|
|
|
|
// Check each builtin function generates the expected result.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#add(NUMVAR,13)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("31"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#div(NUMVAR,3)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("6"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,5)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,99)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("99"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,5)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("5"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,99)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#mul(NUMVAR,3)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("54"), Succeeded());
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#sub(NUMVAR,7)]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("11"), Succeeded());
|
|
|
|
// Check nested function calls.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#add(min(7,2),max(4,10))]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("12"), Succeeded());
|
|
|
|
// Check function call that uses a variable of the same name.
|
|
Tester.initNextPattern();
|
|
ASSERT_FALSE(Tester.parsePattern("[[#add(add,add)+min (add,3)+add]]"));
|
|
EXPECT_THAT_EXPECTED(Tester.match("24"), Succeeded());
|
|
}
|
|
|
|
TEST_F(FileCheckTest, Substitution) {
|
|
SourceMgr SM;
|
|
FileCheckPatternContext Context;
|
|
EXPECT_THAT_ERROR(Context.defineCmdlineVariables({"FOO=BAR"}, SM),
|
|
Succeeded());
|
|
|
|
// Substitution of an undefined string variable fails and error holds that
|
|
// variable's name.
|
|
StringSubstitution StringSubstitution(&Context, "VAR404", 42);
|
|
Expected<std::string> SubstValue = StringSubstitution.getResult();
|
|
expectUndefErrors({"VAR404"}, SubstValue.takeError());
|
|
|
|
// Numeric substitution blocks constituted of defined numeric variables are
|
|
// substituted for the variable's value.
|
|
NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned),
|
|
1);
|
|
NVar.setValue(ExpressionValue(10u));
|
|
auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar);
|
|
auto ExpressionN = std::make_unique<Expression>(
|
|
std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper));
|
|
NumericSubstitution SubstitutionN(&Context, "N", std::move(ExpressionN),
|
|
/*InsertIdx=*/30);
|
|
SubstValue = SubstitutionN.getResult();
|
|
ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
|
|
EXPECT_EQ("A", *SubstValue);
|
|
|
|
// Substitution of an undefined numeric variable fails, error holds name of
|
|
// undefined variable.
|
|
NVar.clearValue();
|
|
SubstValue = SubstitutionN.getResult();
|
|
expectUndefErrors({"N"}, SubstValue.takeError());
|
|
|
|
// Substitution of a defined string variable returns the right value.
|
|
Pattern P(Check::CheckPlain, &Context, 1);
|
|
StringSubstitution = llvm::StringSubstitution(&Context, "FOO", 42);
|
|
SubstValue = StringSubstitution.getResult();
|
|
ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
|
|
EXPECT_EQ("BAR", *SubstValue);
|
|
}
|
|
|
|
TEST_F(FileCheckTest, FileCheckContext) {
|
|
FileCheckPatternContext Cxt;
|
|
SourceMgr SM;
|
|
|
|
// No definition.
|
|
EXPECT_THAT_ERROR(Cxt.defineCmdlineVariables({}, SM), Succeeded());
|
|
|
|
// Missing equal sign.
|
|
expectDiagnosticError("missing equal sign in global definition",
|
|
Cxt.defineCmdlineVariables({"LocalVar"}, SM));
|
|
expectDiagnosticError("missing equal sign in global definition",
|
|
Cxt.defineCmdlineVariables({"#LocalNumVar"}, SM));
|
|
|
|
// Empty variable name.
|
|
expectDiagnosticError("empty variable name",
|
|
Cxt.defineCmdlineVariables({"=18"}, SM));
|
|
expectDiagnosticError("empty variable name",
|
|
Cxt.defineCmdlineVariables({"#=18"}, SM));
|
|
|
|
// Invalid variable name.
|
|
expectDiagnosticError("invalid variable name",
|
|
Cxt.defineCmdlineVariables({"18LocalVar=18"}, SM));
|
|
expectDiagnosticError("invalid variable name",
|
|
Cxt.defineCmdlineVariables({"#18LocalNumVar=18"}, SM));
|
|
|
|
// Name conflict between pattern and numeric variable.
|
|
expectDiagnosticError(
|
|
"string variable with name 'LocalVar' already exists",
|
|
Cxt.defineCmdlineVariables({"LocalVar=18", "#LocalVar=36"}, SM));
|
|
Cxt = FileCheckPatternContext();
|
|
expectDiagnosticError(
|
|
"numeric variable with name 'LocalNumVar' already exists",
|
|
Cxt.defineCmdlineVariables({"#LocalNumVar=18", "LocalNumVar=36"}, SM));
|
|
Cxt = FileCheckPatternContext();
|
|
|
|
// Invalid numeric value for numeric variable.
|
|
expectUndefErrors({"x"}, Cxt.defineCmdlineVariables({"#LocalNumVar=x"}, SM));
|
|
|
|
// Define local variables from command-line.
|
|
std::vector<StringRef> GlobalDefines;
|
|
// Clear local variables to remove dummy numeric variable x that
|
|
// parseNumericSubstitutionBlock would have created and stored in
|
|
// GlobalNumericVariableTable.
|
|
Cxt.clearLocalVars();
|
|
GlobalDefines.emplace_back("LocalVar=FOO");
|
|
GlobalDefines.emplace_back("EmptyVar=");
|
|
GlobalDefines.emplace_back("#LocalNumVar1=18");
|
|
GlobalDefines.emplace_back("#%x,LocalNumVar2=LocalNumVar1+2");
|
|
GlobalDefines.emplace_back("#LocalNumVar3=0xc");
|
|
ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
|
|
|
|
// Create @LINE pseudo numeric variable and check it is present by matching
|
|
// it.
|
|
size_t LineNumber = 1;
|
|
Pattern P(Check::CheckPlain, &Cxt, LineNumber);
|
|
FileCheckRequest Req;
|
|
Cxt.createLineVariable();
|
|
ASSERT_FALSE(P.parsePattern("[[@LINE]]", "CHECK", SM, Req));
|
|
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
|