#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 create(const std::vector &MacroDefinitions) { return std::make_unique(MacroDefinitions, Lex.SourceMgr.get(), Lex.Style, Lex.Allocator, Lex.IdentTable); } std::string expand(MacroExpander &Macros, llvm::StringRef Name, const std::vector &Args = {}) { EXPECT_TRUE(Macros.defined(Name)); return text(Macros.expand(Lex.id(Name), lexArgs(Args))); } llvm::SmallVector lexArgs(const std::vector &Args) { llvm::SmallVector 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 ExpandedFrom; }; void expectAttributes(const TokenList &Tokens, const std::vector &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 Allocator; std::vector> 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 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 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 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 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