llvm-project/llvm/unittests/Support/FileCheckTest.cpp

1002 lines
41 KiB
C++

//===- llvm/unittest/Support/FileCheckTest.cpp - FileCheck tests --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/FileCheck.h"
#include "../lib/Support/FileCheckImpl.h"
#include "llvm/Support/Regex.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <tuple>
#include <unordered_set>
using namespace llvm;
namespace {
class FileCheckTest : public ::testing::Test {};
static StringRef bufferize(SourceMgr &SM, StringRef Str) {
std::unique_ptr<MemoryBuffer> Buffer =
MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
StringRef StrBufferRef = Buffer->getBuffer();
SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
return StrBufferRef;
}
template <typename ErrorT>
static void expectError(StringRef ExpectedMsg, Error Err) {
bool ErrorHandled = false;
EXPECT_THAT_ERROR(handleErrors(std::move(Err),
[&](const ErrorT &E) {
EXPECT_NE(
E.message().find(ExpectedMsg.str()),
std::string::npos);
ErrorHandled = true;
}),
Succeeded());
EXPECT_TRUE(ErrorHandled);
}
static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
expectError<ErrorDiagnostic>(ExpectedMsg, std::move(Err));
}
struct ExpressionFormatParameterisedFixture
: public ::testing::TestWithParam<
std::tuple<ExpressionFormat::Kind, bool, bool>> {
void SetUp() { std::tie(Kind, AllowHex, AllowUpperHex) = GetParam(); }
ExpressionFormat::Kind Kind;
bool AllowHex;
bool AllowUpperHex;
};
TEST_P(ExpressionFormatParameterisedFixture, Format) {
SourceMgr SM;
ExpressionFormat Format(Kind);
Expected<StringRef> WildcardPattern = Format.getWildcardRegex();
ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded());
Regex WildcardRegex(*WildcardPattern);
ASSERT_TRUE(WildcardRegex.isValid());
// Does not match empty string.
EXPECT_FALSE(WildcardRegex.match(""));
// Matches all decimal digits and matches several of them.
SmallVector<StringRef, 4> Matches;
StringRef DecimalDigits = "0123456789";
ASSERT_TRUE(WildcardRegex.match(DecimalDigits, &Matches));
EXPECT_EQ(Matches[0], DecimalDigits);
// Check non digits or digits with wrong casing are not matched.
if (AllowHex) {
StringRef HexOnlyDigits[] = {"abcdef", "ABCDEF"};
StringRef AcceptedHexOnlyDigits =
AllowUpperHex ? HexOnlyDigits[1] : HexOnlyDigits[0];
StringRef RefusedHexOnlyDigits =
AllowUpperHex ? HexOnlyDigits[0] : HexOnlyDigits[1];
ASSERT_TRUE(WildcardRegex.match(AcceptedHexOnlyDigits, &Matches));
EXPECT_EQ(Matches[0], AcceptedHexOnlyDigits);
EXPECT_FALSE(WildcardRegex.match(RefusedHexOnlyDigits));
EXPECT_FALSE(WildcardRegex.match("g"));
EXPECT_FALSE(WildcardRegex.match("G"));
} else {
EXPECT_FALSE(WildcardRegex.match("a"));
EXPECT_FALSE(WildcardRegex.match("A"));
}
Expected<std::string> MatchingString = Format.getMatchingString(0U);
ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
EXPECT_EQ(*MatchingString, "0");
MatchingString = Format.getMatchingString(9U);
ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
EXPECT_EQ(*MatchingString, "9");
Expected<std::string> TenMatchingString = Format.getMatchingString(10U);
ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded());
Expected<std::string> FifteenMatchingString = Format.getMatchingString(15U);
ASSERT_THAT_EXPECTED(FifteenMatchingString, Succeeded());
StringRef ExpectedTenMatchingString, ExpectedFifteenMatchingString;
if (AllowHex) {
if (AllowUpperHex) {
ExpectedTenMatchingString = "A";
ExpectedFifteenMatchingString = "F";
} else {
ExpectedTenMatchingString = "a";
ExpectedFifteenMatchingString = "f";
}
} else {
ExpectedTenMatchingString = "10";
ExpectedFifteenMatchingString = "15";
}
EXPECT_EQ(*TenMatchingString, ExpectedTenMatchingString);
EXPECT_EQ(*FifteenMatchingString, ExpectedFifteenMatchingString);
StringRef BufferizedValidValueStr = bufferize(SM, "0");
Expected<uint64_t> Val =
Format.valueFromStringRepr(BufferizedValidValueStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
EXPECT_EQ(*Val, 0U);
BufferizedValidValueStr = bufferize(SM, "9");
Val = Format.valueFromStringRepr(BufferizedValidValueStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
EXPECT_EQ(*Val, 9U);
StringRef BufferizedTenStr, BufferizedInvalidTenStr, BufferizedFifteenStr;
StringRef TenStr, FifteenStr, InvalidTenStr;
if (AllowHex) {
if (AllowUpperHex) {
TenStr = "A";
FifteenStr = "F";
InvalidTenStr = "a";
} else {
TenStr = "a";
FifteenStr = "f";
InvalidTenStr = "A";
}
} else {
TenStr = "10";
FifteenStr = "15";
InvalidTenStr = "A";
}
BufferizedTenStr = bufferize(SM, TenStr);
Val = Format.valueFromStringRepr(BufferizedTenStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
EXPECT_EQ(*Val, 10U);
BufferizedFifteenStr = bufferize(SM, FifteenStr);
Val = Format.valueFromStringRepr(BufferizedFifteenStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
EXPECT_EQ(*Val, 15U);
// Wrong casing is not tested because valueFromStringRepr() relies on
// StringRef's getAsInteger() which does not allow to restrict casing.
BufferizedInvalidTenStr = bufferize(SM, InvalidTenStr);
expectDiagnosticError(
"unable to represent numeric value",
Format.valueFromStringRepr(bufferize(SM, "G"), SM).takeError());
// Check boolean operator.
EXPECT_TRUE(bool(Format));
}
INSTANTIATE_TEST_CASE_P(
AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture,
::testing::Values(
std::make_tuple(ExpressionFormat::Kind::Unsigned, /*AllowHex=*/false,
/*AllowUpperHex=*/false),
std::make_tuple(ExpressionFormat::Kind::HexLower, /*AllowHex=*/true,
/*AllowUpperHex=*/false),
std::make_tuple(ExpressionFormat::Kind::HexUpper, /*AllowHex=*/true,
/*AllowUpperHex=*/true)), );
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(18).takeError());
EXPECT_FALSE(bool(NoFormat));
}
TEST_F(FileCheckTest, ConflictFormatProperties) {
ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
expectError<StringError>("trying to match value with invalid format",
ConflictFormat.getWildcardRegex().takeError());
expectError<StringError>("trying to match value with invalid format",
ConflictFormat.getMatchingString(18).takeError());
EXPECT_FALSE(bool(ConflictFormat));
}
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);
ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
ExpressionFormat ConflictFormat2(ExpressionFormat::Kind::Conflict);
EXPECT_TRUE(ConflictFormat == ConflictFormat2);
EXPECT_FALSE(ConflictFormat != ConflictFormat2);
}
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 ConflictFormat(ExpressionFormat::Kind::Conflict);
EXPECT_TRUE(ConflictFormat == ExpressionFormat::Kind::Conflict);
EXPECT_FALSE(ConflictFormat != ExpressionFormat::Kind::Conflict);
ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat);
EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);
}
TEST_F(FileCheckTest, Literal) {
// Eval returns the literal's value.
ExpressionLiteral Ten(10);
Expected<uint64_t> Value = Ten.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
EXPECT_EQ(10U, *Value);
EXPECT_EQ(Ten.getImplicitFormat(), ExpressionFormat::Kind::NoFormat);
// Max value can be correctly represented.
ExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
Value = Max.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
EXPECT_EQ(std::numeric_limits<uint64_t>::max(), *Value);
}
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;
}
TEST_F(FileCheckTest, Expression) {
std::unique_ptr<ExpressionLiteral> Ten =
std::make_unique<ExpressionLiteral>(10);
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);
}
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
TEST_F(FileCheckTest, NumericVariable) {
// 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);
EXPECT_EQ(FooVarUse.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
EXPECT_FALSE(FooVar.getValue());
Expected<uint64_t> EvalResult = FooVarUse.eval();
expectUndefErrors({"FOO"}, EvalResult.takeError());
FooVar.setValue(42);
// Defined variable: getValue and eval return value set.
Optional<uint64_t> Value = FooVar.getValue();
ASSERT_TRUE(Value);
EXPECT_EQ(42U, *Value);
EvalResult = FooVarUse.eval();
ASSERT_THAT_EXPECTED(EvalResult, Succeeded());
EXPECT_EQ(42U, *EvalResult);
// Clearing variable: getValue and eval fail. Error returned by eval holds
// the name of the cleared variable.
FooVar.clearValue();
EXPECT_FALSE(FooVar.getValue());
EvalResult = FooVarUse.eval();
expectUndefErrors({"FOO"}, EvalResult.takeError());
}
TEST_F(FileCheckTest, Binop) {
NumericVariable FooVar("FOO",
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
FooVar.setValue(42);
std::unique_ptr<NumericVariableUse> FooVarUse =
std::make_unique<NumericVariableUse>("FOO", &FooVar);
NumericVariable BarVar("BAR",
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
BarVar.setValue(18);
std::unique_ptr<NumericVariableUse> BarVarUse =
std::make_unique<NumericVariableUse>("BAR", &BarVar);
BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
// Defined variables: eval returns right value; implicit format is as
// expected.
Expected<uint64_t> Value = Binop.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
EXPECT_EQ(60U, *Value);
EXPECT_EQ(Binop.getImplicitFormat(), 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.
FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
std::unique_ptr<ExpressionLiteral> Eighteen =
std::make_unique<ExpressionLiteral>(18);
Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(Eighteen));
EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
Eighteen = std::make_unique<ExpressionLiteral>(18);
Binop = BinaryOperation(doAdd, std::move(Eighteen), std::move(FooVarUse));
EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
// Variables with different implicit format conflict.
NumericVariable BazVar("BAZ",
ExpressionFormat(ExpressionFormat::Kind::HexLower), 3);
FooVarUse = std::make_unique<NumericVariableUse>("BAZ", &FooVar);
std::unique_ptr<NumericVariableUse> BazVarUse =
std::make_unique<NumericVariableUse>("BAZ", &BazVar);
Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(BazVarUse));
EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Conflict);
}
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<std::string> GlobalDefines;
GlobalDefines.emplace_back(std::string("#FOO=42"));
GlobalDefines.emplace_back(std::string("BAR=BAZ"));
// An ASSERT_FALSE would make more sense but cannot be used in a
// constructor.
EXPECT_THAT_ERROR(Context.defineCmdlineVariables(GlobalDefines, SM),
Succeeded());
Context.createLineVariable();
// Call parsePattern to have @LINE defined.
P.parsePattern("N/A", "CHECK", SM, Req);
// parsePattern does not expect to be called twice for the same line and
// will set FixedStr and RegExStr incorrectly if it is. Therefore prepare
// a pattern for a different line.
initNextPattern();
}
void initNextPattern() {
P = Pattern(Check::CheckPlain, &Context, ++LineNumber);
}
size_t getLineNumber() const { return LineNumber; }
Expected<std::unique_ptr<Expression>>
parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) {
StringRef ExprBufferRef = bufferize(SM, Expr);
Optional<NumericVariable *> DefinedNumericVariable;
return P.parseNumericSubstitutionBlock(
ExprBufferRef, DefinedNumericVariable, IsLegacyLineExpr, LineNumber,
&Context, SM);
}
bool parsePattern(StringRef Pattern) {
StringRef PatBufferRef = bufferize(SM, Pattern);
return P.parsePattern(PatBufferRef, "CHECK", SM, Req);
}
Expected<size_t> match(StringRef Buffer) {
StringRef BufferRef = bufferize(SM, Buffer);
size_t MatchLen;
return P.match(BufferRef, MatchLen, SM);
}
};
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 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 operand format '%VAR'",
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());
// Valid single operand expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());
// 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 '%VAR'",
Tester.parseSubst("@LINE+%VAR").takeError());
// Invalid legacy @LINE expression with non literal rhs.
expectDiagnosticError(
"invalid operand format '@LINE'",
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("%x, FOO"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, 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(
"variables with conflicting format specifier: need an explicit one",
Tester.parseSubst("FOO+VAR_LOWER_HEX").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 matching an empty expression only matches a number.
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, Substitution) {
SourceMgr SM;
FileCheckPatternContext Context;
std::vector<std::string> GlobalDefines;
GlobalDefines.emplace_back(std::string("FOO=BAR"));
EXPECT_THAT_ERROR(Context.defineCmdlineVariables(GlobalDefines, 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(10);
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;
std::vector<std::string> GlobalDefines;
SourceMgr SM;
// No definition.
EXPECT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
// Missing equal sign.
GlobalDefines.emplace_back(std::string("LocalVar"));
expectDiagnosticError("missing equal sign in global definition",
Cxt.defineCmdlineVariables(GlobalDefines, SM));
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("#LocalNumVar"));
expectDiagnosticError("missing equal sign in global definition",
Cxt.defineCmdlineVariables(GlobalDefines, SM));
// Empty variable name.
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("=18"));
expectDiagnosticError("empty variable name",
Cxt.defineCmdlineVariables(GlobalDefines, SM));
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("#=18"));
expectDiagnosticError("empty variable name",
Cxt.defineCmdlineVariables(GlobalDefines, SM));
// Invalid variable name.
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("18LocalVar=18"));
expectDiagnosticError("invalid variable name",
Cxt.defineCmdlineVariables(GlobalDefines, SM));
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("#18LocalNumVar=18"));
expectDiagnosticError("invalid variable name",
Cxt.defineCmdlineVariables(GlobalDefines, SM));
// Name conflict between pattern and numeric variable.
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("LocalVar=18"));
GlobalDefines.emplace_back(std::string("#LocalVar=36"));
expectDiagnosticError("string variable with name 'LocalVar' already exists",
Cxt.defineCmdlineVariables(GlobalDefines, SM));
Cxt = FileCheckPatternContext();
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
GlobalDefines.emplace_back(std::string("LocalNumVar=36"));
expectDiagnosticError(
"numeric variable with name 'LocalNumVar' already exists",
Cxt.defineCmdlineVariables(GlobalDefines, SM));
Cxt = FileCheckPatternContext();
// Invalid numeric value for numeric variable.
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("#LocalNumVar=x"));
expectUndefErrors({"x"}, Cxt.defineCmdlineVariables(GlobalDefines, SM));
// Define local variables from command-line.
GlobalDefines.clear();
// Clear local variables to remove dummy numeric variable x that
// parseNumericSubstitutionBlock would have created and stored in
// GlobalNumericVariableTable.
Cxt.clearLocalVars();
GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
GlobalDefines.emplace_back(std::string("EmptyVar="));
GlobalDefines.emplace_back(std::string("#LocalNumVar1=18"));
GlobalDefines.emplace_back(std::string("#%x,LocalNumVar2=LocalNumVar1+2"));
GlobalDefines.emplace_back(std::string("#LocalNumVar3=0xc"));
ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
// Create @LINE pseudo numeric variable and check it is present by matching
// it.
size_t LineNumber = 1;
Pattern P(Check::CheckPlain, &Cxt, LineNumber);
FileCheckRequest Req;
Cxt.createLineVariable();
ASSERT_FALSE(P.parsePattern("[[@LINE]]", "CHECK", SM, Req));
size_t MatchLen;
ASSERT_THAT_EXPECTED(P.match("1", MatchLen, SM), Succeeded());
#ifndef NDEBUG
// Recreating @LINE pseudo numeric variable fails.
EXPECT_DEATH(Cxt.createLineVariable(),
"@LINE pseudo numeric variable already created");
#endif
// Check defined variables are present and undefined ones are absent.
StringRef LocalVarStr = "LocalVar";
StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
StringRef LocalNumVar3Ref = bufferize(SM, "LocalNumVar3");
StringRef EmptyVarStr = "EmptyVar";
StringRef UnknownVarStr = "UnknownVar";
Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
Optional<NumericVariable *> DefinedNumericVariable;
Expected<std::unique_ptr<Expression>> ExpressionPointer =
P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, LineNumber,
&Cxt, SM);
ASSERT_THAT_EXPECTED(LocalVar, Succeeded());
EXPECT_EQ(*LocalVar, "FOO");
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
Expected<uint64_t> ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 18U);
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(*ExpressionVal, 20U);
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(*ExpressionVal, 12U);
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(std::string("$GlobalVar=BAR"));
GlobalDefines.emplace_back(std::string("#$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(*ExpressionVal, 36U);
// 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(*ExpressionVal, 36U);
}
} // namespace