208 lines
6.8 KiB
C++
208 lines
6.8 KiB
C++
|
#include "../../lib/Format/Macros.h"
|
||
|
#include "TestLexer.h"
|
||
|
#include "clang/Basic/FileManager.h"
|
||
|
|
||
|
#include "gtest/gtest.h"
|
||
|
|
||
|
namespace clang {
|
||
|
namespace format {
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
class MacroExpanderTest : public ::testing::Test {
|
||
|
public:
|
||
|
MacroExpanderTest() : Lex(Allocator, Buffers) {}
|
||
|
std::unique_ptr<MacroExpander>
|
||
|
create(const std::vector<std::string> &MacroDefinitions) {
|
||
|
return std::make_unique<MacroExpander>(MacroDefinitions,
|
||
|
Lex.SourceMgr.get(), Lex.Style,
|
||
|
Lex.Allocator, Lex.IdentTable);
|
||
|
}
|
||
|
|
||
|
std::string expand(MacroExpander &Macros, llvm::StringRef Name,
|
||
|
const std::vector<std::string> &Args = {}) {
|
||
|
EXPECT_TRUE(Macros.defined(Name));
|
||
|
return text(Macros.expand(Lex.id(Name), lexArgs(Args)));
|
||
|
}
|
||
|
|
||
|
llvm::SmallVector<TokenList, 1>
|
||
|
lexArgs(const std::vector<std::string> &Args) {
|
||
|
llvm::SmallVector<TokenList, 1> Result;
|
||
|
for (const auto &Arg : Args) {
|
||
|
Result.push_back(uneof(Lex.lex(Arg)));
|
||
|
}
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
struct MacroAttributes {
|
||
|
clang::tok::TokenKind Kind;
|
||
|
MacroRole Role;
|
||
|
unsigned Start;
|
||
|
unsigned End;
|
||
|
llvm::SmallVector<FormatToken *, 1> ExpandedFrom;
|
||
|
};
|
||
|
|
||
|
void expectAttributes(const TokenList &Tokens,
|
||
|
const std::vector<MacroAttributes> &Attributes,
|
||
|
const std::string &File, unsigned Line) {
|
||
|
EXPECT_EQ(Tokens.size(), Attributes.size()) << text(Tokens);
|
||
|
for (size_t I = 0, E = Tokens.size(); I != E; ++I) {
|
||
|
if (I >= Attributes.size())
|
||
|
continue;
|
||
|
std::string Context =
|
||
|
("for token " + llvm::Twine(I) + ": " + Tokens[I]->Tok.getName() +
|
||
|
" / " + Tokens[I]->TokenText)
|
||
|
.str();
|
||
|
EXPECT_TRUE(Tokens[I]->is(Attributes[I].Kind))
|
||
|
<< Context << " in " << text(Tokens) << " at " << File << ":" << Line;
|
||
|
EXPECT_EQ(Tokens[I]->MacroCtx->Role, Attributes[I].Role)
|
||
|
<< Context << " in " << text(Tokens) << " at " << File << ":" << Line;
|
||
|
EXPECT_EQ(Tokens[I]->MacroCtx->StartOfExpansion, Attributes[I].Start)
|
||
|
<< Context << " in " << text(Tokens) << " at " << File << ":" << Line;
|
||
|
EXPECT_EQ(Tokens[I]->MacroCtx->EndOfExpansion, Attributes[I].End)
|
||
|
<< Context << " in " << text(Tokens) << " at " << File << ":" << Line;
|
||
|
EXPECT_EQ(Tokens[I]->MacroCtx->ExpandedFrom, Attributes[I].ExpandedFrom)
|
||
|
<< Context << " in " << text(Tokens) << " at " << File << ":" << Line;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
llvm::SpecificBumpPtrAllocator<FormatToken> Allocator;
|
||
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Buffers;
|
||
|
TestLexer Lex;
|
||
|
};
|
||
|
|
||
|
#define EXPECT_ATTRIBUTES(Tokens, Attributes) \
|
||
|
expectAttributes(Tokens, Attributes, __FILE__, __LINE__)
|
||
|
|
||
|
TEST_F(MacroExpanderTest, SkipsDefinitionOnError) {
|
||
|
auto Macros =
|
||
|
create({"A(", "B(,", "C(a,", "D(a a", "E(a, a", "F(,)", "G(a;"});
|
||
|
for (const auto *Name : {"A", "B", "C", "D", "E", "F", "G"}) {
|
||
|
EXPECT_FALSE(Macros->defined(Name)) << "for Name " << Name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TEST_F(MacroExpanderTest, ExpandsWithoutArguments) {
|
||
|
auto Macros = create({
|
||
|
"A",
|
||
|
"B=b",
|
||
|
"C=c + c",
|
||
|
"D()",
|
||
|
});
|
||
|
EXPECT_TRUE(Macros->objectLike("A"));
|
||
|
EXPECT_TRUE(Macros->objectLike("B"));
|
||
|
EXPECT_TRUE(Macros->objectLike("C"));
|
||
|
EXPECT_TRUE(!Macros->objectLike("D"));
|
||
|
EXPECT_EQ("", expand(*Macros, "A"));
|
||
|
EXPECT_EQ("b", expand(*Macros, "B"));
|
||
|
EXPECT_EQ("c+c", expand(*Macros, "C"));
|
||
|
EXPECT_EQ("", expand(*Macros, "D"));
|
||
|
}
|
||
|
|
||
|
TEST_F(MacroExpanderTest, ExpandsWithArguments) {
|
||
|
auto Macros = create({
|
||
|
"A(x)",
|
||
|
"B(x, y)=x + y",
|
||
|
});
|
||
|
EXPECT_EQ("", expand(*Macros, "A", {"a"}));
|
||
|
EXPECT_EQ("b1+b2+b3", expand(*Macros, "B", {"b1", "b2 + b3"}));
|
||
|
EXPECT_EQ("x+", expand(*Macros, "B", {"x"}));
|
||
|
}
|
||
|
|
||
|
TEST_F(MacroExpanderTest, AttributizesTokens) {
|
||
|
auto Macros = create({
|
||
|
"A(x, y)={ x + y; }",
|
||
|
"B(x, y)=x + 3 + y",
|
||
|
});
|
||
|
auto *A = Lex.id("A");
|
||
|
auto AArgs = lexArgs({"a1 * a2", "a3 * a4"});
|
||
|
auto Result = Macros->expand(A, AArgs);
|
||
|
EXPECT_EQ(11U, Result.size()) << text(Result) << " / " << Result;
|
||
|
EXPECT_EQ("{a1*a2+a3*a4;}", text(Result));
|
||
|
std::vector<MacroAttributes> Attributes = {
|
||
|
{tok::l_brace, MR_Hidden, 1, 0, {A}},
|
||
|
{tok::identifier, MR_ExpandedArg, 0, 0, {A}},
|
||
|
{tok::star, MR_ExpandedArg, 0, 0, {A}},
|
||
|
{tok::identifier, MR_ExpandedArg, 0, 0, {A}},
|
||
|
{tok::plus, MR_Hidden, 0, 0, {A}},
|
||
|
{tok::identifier, MR_ExpandedArg, 0, 0, {A}},
|
||
|
{tok::star, MR_ExpandedArg, 0, 0, {A}},
|
||
|
{tok::identifier, MR_ExpandedArg, 0, 0, {A}},
|
||
|
{tok::semi, MR_Hidden, 0, 0, {A}},
|
||
|
{tok::r_brace, MR_Hidden, 0, 1, {A}},
|
||
|
{tok::eof, MR_Hidden, 0, 0, {A}},
|
||
|
};
|
||
|
EXPECT_ATTRIBUTES(Result, Attributes);
|
||
|
|
||
|
auto *B = Lex.id("B");
|
||
|
auto BArgs = lexArgs({"b1", "b2"});
|
||
|
Result = Macros->expand(B, BArgs);
|
||
|
EXPECT_EQ(6U, Result.size()) << text(Result) << " / " << Result;
|
||
|
EXPECT_EQ("b1+3+b2", text(Result));
|
||
|
Attributes = {
|
||
|
{tok::identifier, MR_ExpandedArg, 1, 0, {B}},
|
||
|
{tok::plus, MR_Hidden, 0, 0, {B}},
|
||
|
{tok::numeric_constant, MR_Hidden, 0, 0, {B}},
|
||
|
{tok::plus, MR_Hidden, 0, 0, {B}},
|
||
|
{tok::identifier, MR_ExpandedArg, 0, 1, {B}},
|
||
|
{tok::eof, MR_Hidden, 0, 0, {B}},
|
||
|
};
|
||
|
EXPECT_ATTRIBUTES(Result, Attributes);
|
||
|
}
|
||
|
|
||
|
TEST_F(MacroExpanderTest, RecursiveExpansion) {
|
||
|
auto Macros = create({
|
||
|
"A(x)=x",
|
||
|
"B(x)=x",
|
||
|
"C(x)=x",
|
||
|
});
|
||
|
|
||
|
auto *A = Lex.id("A");
|
||
|
auto *B = Lex.id("B");
|
||
|
auto *C = Lex.id("C");
|
||
|
|
||
|
auto Args = lexArgs({"id"});
|
||
|
auto CResult = uneof(Macros->expand(C, Args));
|
||
|
auto BResult = uneof(Macros->expand(B, CResult));
|
||
|
auto AResult = uneof(Macros->expand(A, BResult));
|
||
|
|
||
|
std::vector<MacroAttributes> Attributes = {
|
||
|
{tok::identifier, MR_ExpandedArg, 3, 3, {C, B, A}},
|
||
|
};
|
||
|
EXPECT_ATTRIBUTES(AResult, Attributes);
|
||
|
}
|
||
|
|
||
|
TEST_F(MacroExpanderTest, SingleExpansion) {
|
||
|
auto Macros = create({"A(x)=x+x"});
|
||
|
auto *A = Lex.id("A");
|
||
|
auto Args = lexArgs({"id"});
|
||
|
auto Result = uneof(Macros->expand(A, Args));
|
||
|
std::vector<MacroAttributes> Attributes = {
|
||
|
{tok::identifier, MR_ExpandedArg, 1, 0, {A}},
|
||
|
{tok::plus, MR_Hidden, 0, 0, {A}},
|
||
|
{tok::identifier, MR_Hidden, 0, 1, {A}},
|
||
|
};
|
||
|
EXPECT_ATTRIBUTES(Result, Attributes);
|
||
|
}
|
||
|
|
||
|
TEST_F(MacroExpanderTest, UnderstandsCppTokens) {
|
||
|
auto Macros = create({"A(T,name)=T name = 0;"});
|
||
|
auto *A = Lex.id("A");
|
||
|
auto Args = lexArgs({"const int", "x"});
|
||
|
auto Result = uneof(Macros->expand(A, Args));
|
||
|
std::vector<MacroAttributes> Attributes = {
|
||
|
{tok::kw_const, MR_ExpandedArg, 1, 0, {A}},
|
||
|
{tok::kw_int, MR_ExpandedArg, 0, 0, {A}},
|
||
|
{tok::identifier, MR_ExpandedArg, 0, 0, {A}},
|
||
|
{tok::equal, MR_Hidden, 0, 0, {A}},
|
||
|
{tok::numeric_constant, MR_Hidden, 0, 0, {A}},
|
||
|
{tok::semi, MR_Hidden, 0, 1, {A}},
|
||
|
};
|
||
|
EXPECT_ATTRIBUTES(Result, Attributes);
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
} // namespace format
|
||
|
} // namespace clang
|