forked from OSchip/llvm-project
686 lines
21 KiB
C++
686 lines
21 KiB
C++
#include "../../lib/Format/Macros.h"
|
|
#include "../../lib/Format/UnwrappedLineParser.h"
|
|
#include "TestLexer.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include <map>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
namespace clang {
|
|
namespace format {
|
|
namespace {
|
|
|
|
using UnexpandedMap =
|
|
llvm::DenseMap<FormatToken *, std::unique_ptr<UnwrappedLine>>;
|
|
|
|
// Keeps track of a sequence of macro expansions.
|
|
//
|
|
// The expanded tokens are accessible via getTokens(), while a map of macro call
|
|
// identifier token to unexpanded token stream is accessible via
|
|
// getUnexpanded().
|
|
class Expansion {
|
|
public:
|
|
Expansion(TestLexer &Lex, MacroExpander &Macros) : Lex(Lex), Macros(Macros) {}
|
|
|
|
// Appends the token stream obtained from expanding the macro Name given
|
|
// the provided arguments, to be later retrieved with getTokens().
|
|
// Returns the list of tokens making up the unexpanded macro call.
|
|
TokenList
|
|
expand(llvm::StringRef Name,
|
|
const SmallVector<llvm::SmallVector<FormatToken *, 8>, 1> &Args) {
|
|
auto *ID = Lex.id(Name);
|
|
auto UnexpandedLine = std::make_unique<UnwrappedLine>();
|
|
UnexpandedLine->Tokens.push_back(ID);
|
|
if (!Args.empty()) {
|
|
UnexpandedLine->Tokens.push_back(Lex.id("("));
|
|
for (auto I = Args.begin(), E = Args.end(); I != E; ++I) {
|
|
if (I != Args.begin())
|
|
UnexpandedLine->Tokens.push_back(Lex.id(","));
|
|
UnexpandedLine->Tokens.insert(UnexpandedLine->Tokens.end(), I->begin(),
|
|
I->end());
|
|
}
|
|
UnexpandedLine->Tokens.push_back(Lex.id(")"));
|
|
}
|
|
Unexpanded[ID] = std::move(UnexpandedLine);
|
|
|
|
auto Expanded = uneof(Macros.expand(ID, Args));
|
|
Tokens.append(Expanded.begin(), Expanded.end());
|
|
|
|
TokenList UnexpandedTokens;
|
|
for (const UnwrappedLineNode &Node : Unexpanded[ID]->Tokens)
|
|
UnexpandedTokens.push_back(Node.Tok);
|
|
return UnexpandedTokens;
|
|
}
|
|
|
|
TokenList expand(llvm::StringRef Name,
|
|
const std::vector<std::string> &Args = {}) {
|
|
return expand(Name, lexArgs(Args));
|
|
}
|
|
|
|
const UnexpandedMap &getUnexpanded() const { return Unexpanded; }
|
|
|
|
const TokenList &getTokens() const { return Tokens; }
|
|
|
|
private:
|
|
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;
|
|
}
|
|
llvm::DenseMap<FormatToken *, std::unique_ptr<UnwrappedLine>> Unexpanded;
|
|
llvm::SmallVector<FormatToken *, 8> Tokens;
|
|
TestLexer &Lex;
|
|
MacroExpander &Macros;
|
|
};
|
|
|
|
struct Chunk {
|
|
Chunk(llvm::ArrayRef<FormatToken *> Tokens)
|
|
: Tokens(Tokens.begin(), Tokens.end()) {}
|
|
Chunk(llvm::ArrayRef<UnwrappedLine> Children)
|
|
: Children(Children.begin(), Children.end()) {}
|
|
llvm::SmallVector<UnwrappedLineNode, 1> Tokens;
|
|
llvm::SmallVector<UnwrappedLine, 0> Children;
|
|
};
|
|
|
|
// Allows to produce chunks of a token list by typing the code of equal tokens.
|
|
//
|
|
// Created from a list of tokens, users call "consume" to get the next chunk
|
|
// of tokens, checking that they match the written code.
|
|
struct Matcher {
|
|
Matcher(const TokenList &Tokens, TestLexer &Lex)
|
|
: Tokens(Tokens), It(this->Tokens.begin()), Lex(Lex) {}
|
|
|
|
bool tokenMatches(const FormatToken *Left, const FormatToken *Right) {
|
|
if (Left->getType() == Right->getType() &&
|
|
Left->TokenText == Right->TokenText) {
|
|
return true;
|
|
}
|
|
llvm::dbgs() << Left->TokenText << " != " << Right->TokenText << "\n";
|
|
return false;
|
|
}
|
|
|
|
Chunk consume(StringRef Tokens) {
|
|
TokenList Result;
|
|
for (const FormatToken *Token : uneof(Lex.lex(Tokens))) {
|
|
(void)Token; // Fix unused variable warning when asserts are disabled.
|
|
assert(tokenMatches(*It, Token));
|
|
Result.push_back(*It);
|
|
++It;
|
|
}
|
|
return Chunk(Result);
|
|
}
|
|
|
|
TokenList Tokens;
|
|
TokenList::iterator It;
|
|
TestLexer &Lex;
|
|
};
|
|
|
|
UnexpandedMap mergeUnexpanded(const UnexpandedMap &M1,
|
|
const UnexpandedMap &M2) {
|
|
UnexpandedMap Result;
|
|
for (const auto &KV : M1)
|
|
Result[KV.first] = std::make_unique<UnwrappedLine>(*KV.second);
|
|
for (const auto &KV : M2)
|
|
Result[KV.first] = std::make_unique<UnwrappedLine>(*KV.second);
|
|
return Result;
|
|
}
|
|
|
|
class MacroCallReconstructorTest : public ::testing::Test {
|
|
public:
|
|
MacroCallReconstructorTest() : Lex(Allocator, Buffers) {}
|
|
|
|
std::unique_ptr<MacroExpander>
|
|
createExpander(const std::vector<std::string> &MacroDefinitions) {
|
|
return std::make_unique<MacroExpander>(MacroDefinitions,
|
|
Lex.SourceMgr.get(), Lex.Style,
|
|
Lex.Allocator, Lex.IdentTable);
|
|
}
|
|
|
|
UnwrappedLine line(llvm::ArrayRef<FormatToken *> Tokens) {
|
|
UnwrappedLine Result;
|
|
for (FormatToken *Tok : Tokens)
|
|
Result.Tokens.push_back(UnwrappedLineNode(Tok));
|
|
return Result;
|
|
}
|
|
|
|
UnwrappedLine line(llvm::StringRef Text) { return line({lex(Text)}); }
|
|
|
|
UnwrappedLine line(llvm::ArrayRef<Chunk> Chunks) {
|
|
UnwrappedLine Result;
|
|
for (const Chunk &Chunk : Chunks) {
|
|
Result.Tokens.insert(Result.Tokens.end(), Chunk.Tokens.begin(),
|
|
Chunk.Tokens.end());
|
|
assert(!Result.Tokens.empty());
|
|
Result.Tokens.back().Children.append(Chunk.Children.begin(),
|
|
Chunk.Children.end());
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
TokenList lex(llvm::StringRef Text) { return uneof(Lex.lex(Text)); }
|
|
|
|
Chunk tokens(llvm::StringRef Text) { return Chunk(lex(Text)); }
|
|
|
|
Chunk children(llvm::ArrayRef<UnwrappedLine> Children) {
|
|
return Chunk(Children);
|
|
}
|
|
|
|
llvm::SpecificBumpPtrAllocator<FormatToken> Allocator;
|
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Buffers;
|
|
TestLexer Lex;
|
|
};
|
|
|
|
bool matchesTokens(const UnwrappedLine &L1, const UnwrappedLine &L2) {
|
|
if (L1.Tokens.size() != L2.Tokens.size())
|
|
return false;
|
|
for (auto L1It = L1.Tokens.begin(), L2It = L2.Tokens.begin();
|
|
L1It != L1.Tokens.end(); ++L1It, ++L2It) {
|
|
if (L1It->Tok != L2It->Tok)
|
|
return false;
|
|
if (L1It->Children.size() != L2It->Children.size())
|
|
return false;
|
|
for (auto L1ChildIt = L1It->Children.begin(),
|
|
L2ChildIt = L2It->Children.begin();
|
|
L1ChildIt != L1It->Children.end(); ++L1ChildIt, ++L2ChildIt) {
|
|
if (!matchesTokens(*L1ChildIt, *L2ChildIt))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
MATCHER_P(matchesLine, line, "") { return matchesTokens(arg, line); }
|
|
|
|
TEST_F(MacroCallReconstructorTest, Identifier) {
|
|
auto Macros = createExpander({"X=x"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("X");
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Unexp.addLine(line(Exp.getTokens()));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(line(U.consume("X"))));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, NestedLineWithinCall) {
|
|
auto Macros = createExpander({"C(a)=class X { a; };"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("C", {"void f()"});
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
Unexp.addLine(line(E.consume("class X {")));
|
|
EXPECT_FALSE(Unexp.finished());
|
|
Unexp.addLine(line(E.consume("void f();")));
|
|
EXPECT_FALSE(Unexp.finished());
|
|
Unexp.addLine(line(E.consume("};")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
EXPECT_THAT(std::move(Unexp).takeResult(),
|
|
matchesLine(line(U.consume("C(void f())"))));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, MultipleLinesInNestedMultiParamsExpansion) {
|
|
auto Macros = createExpander({"C(a, b)=a b", "B(a)={a}"});
|
|
Expansion Exp1(Lex, *Macros);
|
|
TokenList Call1 = Exp1.expand("B", {"b"});
|
|
Expansion Exp2(Lex, *Macros);
|
|
TokenList Call2 = Exp2.expand("C", {uneof(Lex.lex("a")), Exp1.getTokens()});
|
|
|
|
UnexpandedMap Unexpanded =
|
|
mergeUnexpanded(Exp1.getUnexpanded(), Exp2.getUnexpanded());
|
|
MacroCallReconstructor Unexp(0, Unexpanded);
|
|
Matcher E(Exp2.getTokens(), Lex);
|
|
Unexp.addLine(line(E.consume("a")));
|
|
EXPECT_FALSE(Unexp.finished());
|
|
Unexp.addLine(line(E.consume("{")));
|
|
EXPECT_FALSE(Unexp.finished());
|
|
Unexp.addLine(line(E.consume("b")));
|
|
EXPECT_FALSE(Unexp.finished());
|
|
Unexp.addLine(line(E.consume("}")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
|
|
Matcher U1(Call1, Lex);
|
|
auto Middle = U1.consume("B(b)");
|
|
Matcher U2(Call2, Lex);
|
|
auto Chunk1 = U2.consume("C(a, ");
|
|
auto Chunk2 = U2.consume("{ b }");
|
|
auto Chunk3 = U2.consume(")");
|
|
|
|
EXPECT_THAT(std::move(Unexp).takeResult(),
|
|
matchesLine(line({Chunk1, Middle, Chunk3})));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, StatementSequence) {
|
|
auto Macros = createExpander({"SEMI=;"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call1 = Exp.expand("SEMI");
|
|
TokenList Call2 = Exp.expand("SEMI");
|
|
TokenList Call3 = Exp.expand("SEMI");
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
Unexp.addLine(line(E.consume(";")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Unexp.addLine(line(E.consume(";")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Unexp.addLine(line(E.consume(";")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U1(Call1, Lex);
|
|
Matcher U2(Call2, Lex);
|
|
Matcher U3(Call3, Lex);
|
|
EXPECT_THAT(std::move(Unexp).takeResult(),
|
|
matchesLine(line(
|
|
{U1.consume("SEMI"),
|
|
children({line({U2.consume("SEMI"),
|
|
children({line(U3.consume("SEMI"))})})})})));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, NestedBlock) {
|
|
auto Macros = createExpander({"ID(x)=x"});
|
|
// Test: ID({ ID(a *b); })
|
|
// 1. expand ID(a *b) -> a *b
|
|
Expansion Exp1(Lex, *Macros);
|
|
TokenList Call1 = Exp1.expand("ID", {"a *b"});
|
|
// 2. expand ID({ a *b; })
|
|
TokenList Arg;
|
|
Arg.push_back(Lex.id("{"));
|
|
Arg.append(Exp1.getTokens().begin(), Exp1.getTokens().end());
|
|
Arg.push_back(Lex.id(";"));
|
|
Arg.push_back(Lex.id("}"));
|
|
Expansion Exp2(Lex, *Macros);
|
|
TokenList Call2 = Exp2.expand("ID", {Arg});
|
|
|
|
// Consume as-if formatted:
|
|
// {
|
|
// a *b;
|
|
// }
|
|
UnexpandedMap Unexpanded =
|
|
mergeUnexpanded(Exp1.getUnexpanded(), Exp2.getUnexpanded());
|
|
MacroCallReconstructor Unexp(0, Unexpanded);
|
|
Matcher E(Exp2.getTokens(), Lex);
|
|
Unexp.addLine(line(E.consume("{")));
|
|
EXPECT_FALSE(Unexp.finished());
|
|
Unexp.addLine(line(E.consume("a *b;")));
|
|
EXPECT_FALSE(Unexp.finished());
|
|
Unexp.addLine(line(E.consume("}")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
|
|
// Expect lines:
|
|
// ID({
|
|
// ID(a *b);
|
|
// })
|
|
Matcher U1(Call1, Lex);
|
|
Matcher U2(Call2, Lex);
|
|
auto Chunk2Start = U2.consume("ID(");
|
|
auto Chunk2LBrace = U2.consume("{");
|
|
U2.consume("a *b");
|
|
auto Chunk2Mid = U2.consume(";");
|
|
auto Chunk2RBrace = U2.consume("}");
|
|
auto Chunk2End = U2.consume(")");
|
|
auto Chunk1 = U1.consume("ID(a *b)");
|
|
|
|
auto Expected = line({Chunk2Start,
|
|
children({
|
|
line(Chunk2LBrace),
|
|
line({Chunk1, Chunk2Mid}),
|
|
line(Chunk2RBrace),
|
|
}),
|
|
Chunk2End});
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, NestedChildBlocks) {
|
|
auto Macros = createExpander({"ID(x)=x", "CALL(x)=f([] { x })"});
|
|
// Test: ID(CALL(CALL(return a * b;)))
|
|
// 1. expand CALL(return a * b;)
|
|
Expansion Exp1(Lex, *Macros);
|
|
TokenList Call1 = Exp1.expand("CALL", {"return a * b;"});
|
|
// 2. expand CALL(f([] { return a * b; }))
|
|
Expansion Exp2(Lex, *Macros);
|
|
TokenList Call2 = Exp2.expand("CALL", {Exp1.getTokens()});
|
|
// 3. expand ID({ f([] { f([] { return a * b; }) }) })
|
|
TokenList Arg3;
|
|
Arg3.push_back(Lex.id("{"));
|
|
Arg3.append(Exp2.getTokens().begin(), Exp2.getTokens().end());
|
|
Arg3.push_back(Lex.id("}"));
|
|
Expansion Exp3(Lex, *Macros);
|
|
TokenList Call3 = Exp3.expand("ID", {Arg3});
|
|
|
|
// Consume as-if formatted in three unwrapped lines:
|
|
// 0: {
|
|
// 1: f([] {
|
|
// f([] {
|
|
// return a * b;
|
|
// })
|
|
// })
|
|
// 2: }
|
|
UnexpandedMap Unexpanded = mergeUnexpanded(
|
|
Exp1.getUnexpanded(),
|
|
mergeUnexpanded(Exp2.getUnexpanded(), Exp3.getUnexpanded()));
|
|
MacroCallReconstructor Unexp(0, Unexpanded);
|
|
Matcher E(Exp3.getTokens(), Lex);
|
|
Unexp.addLine(line(E.consume("{")));
|
|
Unexp.addLine(
|
|
line({E.consume("f([] {"),
|
|
children({line({E.consume("f([] {"),
|
|
children({line(E.consume("return a * b;"))}),
|
|
E.consume("})")})}),
|
|
E.consume("})")}));
|
|
Unexp.addLine(line(E.consume("}")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
|
|
// Expect lines:
|
|
// ID(
|
|
// {
|
|
// CALL(CALL(return a * b;))
|
|
// }
|
|
// )
|
|
Matcher U1(Call1, Lex);
|
|
Matcher U2(Call2, Lex);
|
|
Matcher U3(Call3, Lex);
|
|
auto Chunk3Start = U3.consume("ID(");
|
|
auto Chunk3LBrace = U3.consume("{");
|
|
U3.consume("f([] { f([] { return a * b; }) })");
|
|
auto Chunk3RBrace = U3.consume("}");
|
|
auto Chunk3End = U3.consume(")");
|
|
auto Chunk2Start = U2.consume("CALL(");
|
|
U2.consume("f([] { return a * b; })");
|
|
auto Chunk2End = U2.consume(")");
|
|
auto Chunk1 = U1.consume("CALL(return a * b;)");
|
|
|
|
auto Expected = line({
|
|
Chunk3Start,
|
|
children({
|
|
line(Chunk3LBrace),
|
|
line({
|
|
Chunk2Start,
|
|
Chunk1,
|
|
Chunk2End,
|
|
}),
|
|
line(Chunk3RBrace),
|
|
}),
|
|
Chunk3End,
|
|
});
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, NestedChildrenMultipleArguments) {
|
|
auto Macros = createExpander({"CALL(a, b)=f([] { a; b; })"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("CALL", {std::string("int a"), "int b"});
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
Unexp.addLine(line({
|
|
E.consume("f([] {"),
|
|
children({
|
|
line(E.consume("int a;")),
|
|
line(E.consume("int b;")),
|
|
}),
|
|
E.consume("})"),
|
|
}));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
auto Expected = line(U.consume("CALL(int a, int b)"));
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, ReverseOrderArgumentsInExpansion) {
|
|
auto Macros = createExpander({"CALL(a, b)=b + a"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("CALL", {std::string("x"), "y"});
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
Unexp.addLine(line(E.consume("y + x")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
auto Expected = line(U.consume("CALL(x, y)"));
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, MultipleToplevelUnwrappedLines) {
|
|
auto Macros = createExpander({"ID(a, b)=a b"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("ID", {std::string("x; x"), "y"});
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
Unexp.addLine(line(E.consume("x;")));
|
|
Unexp.addLine(line(E.consume("x y")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
auto Expected = line({
|
|
U.consume("ID("),
|
|
children({
|
|
line(U.consume("x;")),
|
|
line(U.consume("x")),
|
|
}),
|
|
U.consume(", y)"),
|
|
});
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, NestedCallsMultipleLines) {
|
|
auto Macros = createExpander({"ID(x)=x"});
|
|
// Test: ID({ID(a * b);})
|
|
// 1. expand ID(a * b)
|
|
Expansion Exp1(Lex, *Macros);
|
|
TokenList Call1 = Exp1.expand("ID", {"a * b"});
|
|
// 2. expand ID({ a * b; })
|
|
Expansion Exp2(Lex, *Macros);
|
|
TokenList Arg2;
|
|
Arg2.push_back(Lex.id("{"));
|
|
Arg2.append(Exp1.getTokens().begin(), Exp1.getTokens().end());
|
|
Arg2.push_back(Lex.id(";"));
|
|
Arg2.push_back(Lex.id("}"));
|
|
TokenList Call2 = Exp2.expand("ID", {Arg2});
|
|
|
|
// Consume as-if formatted in three unwrapped lines:
|
|
// 0: {
|
|
// 1: a * b;
|
|
// 2: }
|
|
UnexpandedMap Unexpanded =
|
|
mergeUnexpanded(Exp1.getUnexpanded(), Exp2.getUnexpanded());
|
|
MacroCallReconstructor Unexp(0, Unexpanded);
|
|
Matcher E(Exp2.getTokens(), Lex);
|
|
Unexp.addLine(line(E.consume("{")));
|
|
Unexp.addLine(line(E.consume("a * b;")));
|
|
Unexp.addLine(line(E.consume("}")));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
|
|
// Expect lines:
|
|
// ID(
|
|
// {
|
|
// ID(a * b);
|
|
// }
|
|
// )
|
|
Matcher U1(Call1, Lex);
|
|
Matcher U2(Call2, Lex);
|
|
auto Chunk2Start = U2.consume("ID(");
|
|
auto Chunk2LBrace = U2.consume("{");
|
|
U2.consume("a * b");
|
|
auto Chunk2Semi = U2.consume(";");
|
|
auto Chunk2RBrace = U2.consume("}");
|
|
auto Chunk2End = U2.consume(")");
|
|
auto Chunk1 = U1.consume("ID(a * b)");
|
|
|
|
auto Expected = line({
|
|
Chunk2Start,
|
|
children({
|
|
line({Chunk2LBrace}),
|
|
line({Chunk1, Chunk2Semi}),
|
|
line({Chunk2RBrace}),
|
|
}),
|
|
Chunk2End,
|
|
});
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, ParentOutsideMacroCall) {
|
|
auto Macros = createExpander({"ID(a)=a"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("ID", {std::string("x; y; z;")});
|
|
|
|
auto Prefix = tokens("int a = []() {");
|
|
auto Postfix = tokens("}();");
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
Unexp.addLine(line({
|
|
Prefix,
|
|
children({
|
|
line(E.consume("x;")),
|
|
line(E.consume("y;")),
|
|
line(E.consume("z;")),
|
|
}),
|
|
Postfix,
|
|
}));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
auto Expected = line({
|
|
Prefix,
|
|
children({
|
|
line({
|
|
U.consume("ID("),
|
|
children({
|
|
line(U.consume("x;")),
|
|
line(U.consume("y;")),
|
|
line(U.consume("z;")),
|
|
}),
|
|
U.consume(")"),
|
|
}),
|
|
}),
|
|
Postfix,
|
|
});
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, UnusedMacroArguments) {
|
|
auto Macros = createExpander({"X=x"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("X", {"a", "b", "c"});
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Unexp.addLine(line(Exp.getTokens()));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
EXPECT_THAT(std::move(Unexp).takeResult(),
|
|
matchesLine(line(U.consume("X(a, b, c)"))));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, UnusedEmptyMacroArgument) {
|
|
auto Macros = createExpander({"X=x"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("X", {std::string("")});
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
auto Semi = tokens(";");
|
|
Unexp.addLine(line({E.consume("x"), Semi}));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
EXPECT_THAT(std::move(Unexp).takeResult(),
|
|
matchesLine(line({U.consume("X()"), Semi})));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, ChildrenSplitAcrossArguments) {
|
|
auto Macros = createExpander({"CALL(a, b)=f([]() a b)"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("CALL", {std::string("{ a;"), "b; }"});
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
Unexp.addLine(line({
|
|
E.consume("f([]() {"),
|
|
children({
|
|
line(E.consume("a;")),
|
|
line(E.consume("b;")),
|
|
}),
|
|
E.consume("})"),
|
|
}));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
auto Expected = line({
|
|
U.consume("CALL({"),
|
|
children(line(U.consume("a;"))),
|
|
U.consume(", b; })"),
|
|
});
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, ChildrenAfterMacroCall) {
|
|
auto Macros = createExpander({"CALL(a, b)=f([]() a b"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("CALL", {std::string("{ a"), "b"});
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
auto Semi = tokens(";");
|
|
auto SecondLine = tokens("c d;");
|
|
auto ThirdLine = tokens("e f;");
|
|
auto Postfix = tokens("})");
|
|
Unexp.addLine(line({
|
|
E.consume("f([]() {"),
|
|
children({
|
|
line({E.consume("a b"), Semi}),
|
|
line(SecondLine),
|
|
line(ThirdLine),
|
|
}),
|
|
Postfix,
|
|
}));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
auto Expected = line({
|
|
U.consume("CALL({"),
|
|
children(line(U.consume("a"))),
|
|
U.consume(", b)"),
|
|
Semi,
|
|
children(line({
|
|
SecondLine,
|
|
children(line({
|
|
ThirdLine,
|
|
Postfix,
|
|
})),
|
|
})),
|
|
});
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
TEST_F(MacroCallReconstructorTest, InvalidCodeSplittingBracesAcrossArgs) {
|
|
auto Macros = createExpander({"M(a, b)=(a) (b)"});
|
|
Expansion Exp(Lex, *Macros);
|
|
TokenList Call = Exp.expand("M", {std::string("{"), "x", ""});
|
|
|
|
MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
|
|
Matcher E(Exp.getTokens(), Lex);
|
|
auto Prefix = tokens("({");
|
|
Unexp.addLine(line({
|
|
Prefix,
|
|
children({
|
|
line({
|
|
E.consume("({"),
|
|
children({line(E.consume(")(x)"))}),
|
|
}),
|
|
}),
|
|
}));
|
|
EXPECT_TRUE(Unexp.finished());
|
|
Matcher U(Call, Lex);
|
|
auto Expected = line({
|
|
Prefix,
|
|
children({line(U.consume("M({,x,)"))}),
|
|
});
|
|
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace format
|
|
} // namespace clang
|