| #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 |