diff --git a/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h b/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h --- a/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h +++ b/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h @@ -30,6 +30,7 @@ #include "clang-pseudo/Token.h" #include "clang/Basic/TokenKinds.h" +#include #include namespace clang { @@ -86,7 +87,7 @@ }; /// Some piece of the file. {One of Code, Directive, Conditional}. - class Chunk; // Defined below. + using Chunk = std::variant; std::vector Chunks; /// Extract preprocessor structure by examining the raw tokens. @@ -99,7 +100,6 @@ TokenStream stripDirectives(const TokenStream &) const; }; llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DirectiveTree &); -llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DirectiveTree::Chunk &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DirectiveTree::Code &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DirectiveTree::Directive &); @@ -124,48 +124,6 @@ /// The choices are stored in Conditional::Taken nodes. void chooseConditionalBranches(DirectiveTree &, const TokenStream &Code); -// FIXME: This approximates std::variant. -// Switch once we can use C++17. -class DirectiveTree::Chunk { -public: - enum Kind { K_Empty, K_Code, K_Directive, K_Conditional }; - Kind kind() const { - return CodeVariant ? K_Code - : DirectiveVariant ? K_Directive - : ConditionalVariant ? K_Conditional - : K_Empty; - } - - Chunk() = delete; - Chunk(const Chunk &) = delete; - Chunk(Chunk &&) = default; - Chunk &operator=(const Chunk &) = delete; - Chunk &operator=(Chunk &&) = default; - ~Chunk() = default; - - // T => Chunk constructor. - Chunk(Code C) : CodeVariant(std::move(C)) {} - Chunk(Directive C) : DirectiveVariant(std::move(C)) {} - Chunk(Conditional C) : ConditionalVariant(std::move(C)) {} - - // Chunk => T& and const T& conversions. -#define CONVERSION(CONST, V) \ - explicit operator CONST V &() CONST { return *V##Variant; } - CONVERSION(const, Code); - CONVERSION(, Code); - CONVERSION(const, Directive); - CONVERSION(, Directive); - CONVERSION(const, Conditional); - CONVERSION(, Conditional); -#undef CONVERSION - -private: - // Wasteful, a union variant would be better! - llvm::Optional CodeVariant; - llvm::Optional DirectiveVariant; - llvm::Optional ConditionalVariant; -}; - } // namespace pseudo } // namespace clang diff --git a/clang-tools-extra/pseudo/lib/DirectiveTree.cpp b/clang-tools-extra/pseudo/lib/DirectiveTree.cpp --- a/clang-tools-extra/pseudo/lib/DirectiveTree.cpp +++ b/clang-tools-extra/pseudo/lib/DirectiveTree.cpp @@ -10,6 +10,7 @@ #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/TokenKinds.h" #include "llvm/Support/FormatVariadic.h" +#include namespace clang { namespace pseudo { @@ -141,6 +142,36 @@ clang::IdentifierTable PPKeywords; }; +struct Dumper { + llvm::raw_ostream &OS; + unsigned Indent = 0; + + Dumper(llvm::raw_ostream& OS) : OS(OS) {} + void operator()(const DirectiveTree& Tree) { + for (const auto& Chunk : Tree.Chunks) + std::visit(*this, Chunk); + } + void operator()(const DirectiveTree::Conditional &Conditional) { + for (unsigned I = 0; I < Conditional.Branches.size(); ++I) { + const auto &Branch = Conditional.Branches[I]; + (*this)(Branch.first, Conditional.Taken == I); + Indent += 2; + (*this)(Branch.second); + Indent -= 2; + } + (*this)(Conditional.End); + } + void operator()(const DirectiveTree::Directive &Directive, + bool Taken = false) { + OS.indent(Indent) << llvm::formatv( + "#{0} ({1} tokens){2}\n", tok::getPPKeywordSpelling(Directive.Kind), + Directive.Tokens.size(), Taken ? " TAKEN" : ""); + } + void operator()(const DirectiveTree::Code &Code) { + OS.indent(Indent) << llvm::formatv("code ({0} tokens)\n", + Code.Tokens.size()); + } +}; } // namespace DirectiveTree DirectiveTree::parse(const TokenStream &Code) { @@ -149,57 +180,13 @@ return Result; } -static void dump(llvm::raw_ostream &OS, const DirectiveTree &, unsigned Indent); -static void dump(llvm::raw_ostream &OS, - const DirectiveTree::Directive &Directive, unsigned Indent, - bool Taken = false) { - OS.indent(Indent) << llvm::formatv( - "#{0} ({1} tokens){2}\n", tok::getPPKeywordSpelling(Directive.Kind), - Directive.Tokens.size(), Taken ? " TAKEN" : ""); -} -static void dump(llvm::raw_ostream &OS, const DirectiveTree::Code &Code, - unsigned Indent) { - OS.indent(Indent) << llvm::formatv("code ({0} tokens)\n", Code.Tokens.size()); -} -static void dump(llvm::raw_ostream &OS, - const DirectiveTree::Conditional &Conditional, - unsigned Indent) { - for (unsigned I = 0; I < Conditional.Branches.size(); ++I) { - const auto &Branch = Conditional.Branches[I]; - dump(OS, Branch.first, Indent, Conditional.Taken == I); - dump(OS, Branch.second, Indent + 2); - } - dump(OS, Conditional.End, Indent); -} - -static void dump(llvm::raw_ostream &OS, const DirectiveTree::Chunk &Chunk, - unsigned Indent) { - switch (Chunk.kind()) { - case DirectiveTree::Chunk::K_Empty: - llvm_unreachable("invalid chunk"); - case DirectiveTree::Chunk::K_Code: - return dump(OS, (const DirectiveTree::Code &)Chunk, Indent); - case DirectiveTree::Chunk::K_Directive: - return dump(OS, (const DirectiveTree::Directive &)Chunk, Indent); - case DirectiveTree::Chunk::K_Conditional: - return dump(OS, (const DirectiveTree::Conditional &)Chunk, Indent); - } -} - -static void dump(llvm::raw_ostream &OS, const DirectiveTree &Tree, - unsigned Indent) { - for (const auto &Chunk : Tree.Chunks) - dump(OS, Chunk, Indent); -} - // Define operator<< in terms of dump() functions above. #define OSTREAM_DUMP(Type) \ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Type &T) { \ - dump(OS, T, 0); \ + Dumper{OS}(T); \ return OS; \ } OSTREAM_DUMP(DirectiveTree) -OSTREAM_DUMP(DirectiveTree::Chunk) OSTREAM_DUMP(DirectiveTree::Directive) OSTREAM_DUMP(DirectiveTree::Conditional) OSTREAM_DUMP(DirectiveTree::Code) @@ -223,9 +210,6 @@ public: BranchChooser(const TokenStream &Code) : Code(Code) {} - void choose(DirectiveTree &M) { walk(M); } - -private: // Describes code seen by making particular branch choices. Higher is better. struct Score { int Tokens = 0; // excluding comments and directives @@ -246,7 +230,7 @@ } }; - Score walk(DirectiveTree::Code &C) { + Score operator()(DirectiveTree::Code &C) { Score S; for (const Token &T : Code.tokens(C.Tokens)) if (T.Kind != tok::comment) @@ -254,35 +238,14 @@ return S; } - Score walk(DirectiveTree::Directive &D) { + Score operator()(DirectiveTree::Directive &D) { Score S; S.Directives = 1; S.Errors = D.Kind == tok::pp_error; return S; } - Score walk(DirectiveTree::Chunk &C) { - switch (C.kind()) { - case DirectiveTree::Chunk::K_Code: - return walk((DirectiveTree::Code &)C); - case DirectiveTree::Chunk::K_Directive: - return walk((DirectiveTree::Directive &)C); - case DirectiveTree::Chunk::K_Conditional: - return walk((DirectiveTree::Conditional &)C); - case DirectiveTree::Chunk::K_Empty: - break; - } - llvm_unreachable("bad chunk kind"); - } - - Score walk(DirectiveTree &M) { - Score S; - for (DirectiveTree::Chunk &C : M.Chunks) - S += walk(C); - return S; - } - - Score walk(DirectiveTree::Conditional &C) { + Score operator()(DirectiveTree::Conditional &C) { Score Best; bool MayTakeTrivial = true; bool TookTrivial = false; @@ -311,7 +274,14 @@ } return Best; } + Score walk(DirectiveTree &M) { + Score S; + for (auto &C : M.Chunks) + S += std::visit(*this, C); + return S; + } +private: // Return true if the directive starts an always-taken conditional branch, // false if the branch is never taken, and None otherwise. llvm::Optional isTakenWhenReached(const DirectiveTree::Directive &Dir) { @@ -344,7 +314,7 @@ } // namespace void chooseConditionalBranches(DirectiveTree &Tree, const TokenStream &Code) { - BranchChooser{Code}.choose(Tree); + BranchChooser{Code}.walk(Tree); } namespace { @@ -358,31 +328,17 @@ void walk(const DirectiveTree &T) { for (const auto &C : T.Chunks) - walk(C); - } - - void walk(const DirectiveTree::Chunk &C) { - switch (C.kind()) { - case DirectiveTree::Chunk::K_Code: - return walk((const DirectiveTree::Code &)C); - case DirectiveTree::Chunk::K_Directive: - return walk((const DirectiveTree::Directive &)C); - case DirectiveTree::Chunk::K_Conditional: - return walk((const DirectiveTree::Conditional &)C); - case DirectiveTree::Chunk::K_Empty: - break; - } - llvm_unreachable("bad chunk kind"); + std::visit(*this, C); } - void walk(const DirectiveTree::Code &C) { + void operator()(const DirectiveTree::Code &C) { for (const auto &Tok : In.tokens(C.Tokens)) Out.push(Tok); } - void walk(const DirectiveTree::Directive &) {} + void operator()(const DirectiveTree::Directive &) {} - void walk(const DirectiveTree::Conditional &C) { + void operator()(const DirectiveTree::Conditional &C) { if (C.Taken) walk(C.Branches[*C.Taken].second); } diff --git a/clang-tools-extra/pseudo/unittests/DirectiveTreeTest.cpp b/clang-tools-extra/pseudo/unittests/DirectiveTreeTest.cpp --- a/clang-tools-extra/pseudo/unittests/DirectiveTreeTest.cpp +++ b/clang-tools-extra/pseudo/unittests/DirectiveTreeTest.cpp @@ -44,7 +44,15 @@ return testing::Matches(tokens(Tokens))(TS.tokens(arg.Tokens)); } -MATCHER_P(chunkKind, K, "") { return arg.kind() == K; } +MATCHER(directiveChunk, "") { + return std::holds_alternative(arg); +} +MATCHER(codeChunk, "") { + return std::holds_alternative(arg); +} +MATCHER(conditionalChunk, "") { + return std::holds_alternative(arg); +} TEST(DirectiveTree, Parse) { LangOptions Opts; @@ -66,19 +74,16 @@ TokenStream S = cook(lex(Code, Opts), Opts); DirectiveTree PP = DirectiveTree::parse(S); + ASSERT_THAT(PP.Chunks, ElementsAre(directiveChunk(), codeChunk(), + conditionalChunk(), codeChunk())); - ASSERT_THAT(PP.Chunks, ElementsAre(chunkKind(Chunk::K_Directive), - chunkKind(Chunk::K_Code), - chunkKind(Chunk::K_Conditional), - chunkKind(Chunk::K_Code))); - - EXPECT_THAT((const DirectiveTree::Directive &)PP.Chunks[0], + EXPECT_THAT(std::get(PP.Chunks[0]), tokensAre(S, "# include < foo . h >")); - EXPECT_THAT((const DirectiveTree::Code &)PP.Chunks[1], + EXPECT_THAT(std::get(PP.Chunks[1]), tokensAre(S, "int main ( ) {")); - EXPECT_THAT((const DirectiveTree::Code &)PP.Chunks[3], tokensAre(S, "}")); + EXPECT_THAT(std::get(PP.Chunks[3]), tokensAre(S, "}")); - const DirectiveTree::Conditional &Ifdef(PP.Chunks[2]); + const auto &Ifdef = std::get(PP.Chunks[2]); EXPECT_THAT(Ifdef.Branches, ElementsAre(Pair(tokensAre(S, "# ifdef HAS_FOO"), _), Pair(tokensAre(S, "# elif NEEDS_FOO"), _))); @@ -87,17 +92,15 @@ const DirectiveTree &HasFoo(Ifdef.Branches[0].second); const DirectiveTree &NeedsFoo(Ifdef.Branches[1].second); - EXPECT_THAT(HasFoo.Chunks, ElementsAre(chunkKind(Chunk::K_Conditional))); - const DirectiveTree::Conditional &If(HasFoo.Chunks[0]); + EXPECT_THAT(HasFoo.Chunks, ElementsAre(conditionalChunk())); + const auto &If = std::get(HasFoo.Chunks[0]); EXPECT_THAT(If.Branches, ElementsAre(Pair(tokensAre(S, "# if HAS_BAR"), _), Pair(tokensAre(S, "# else"), _))); - EXPECT_THAT(If.Branches[0].second.Chunks, - ElementsAre(chunkKind(Chunk::K_Code))); - EXPECT_THAT(If.Branches[1].second.Chunks, - ElementsAre(chunkKind(Chunk::K_Code))); + EXPECT_THAT(If.Branches[0].second.Chunks, ElementsAre(codeChunk())); + EXPECT_THAT(If.Branches[1].second.Chunks, ElementsAre(codeChunk())); - EXPECT_THAT(NeedsFoo.Chunks, ElementsAre(chunkKind(Chunk::K_Directive))); - const DirectiveTree::Directive &Error(NeedsFoo.Chunks[0]); + EXPECT_THAT(NeedsFoo.Chunks, ElementsAre(directiveChunk())); + const auto &Error = std::get(NeedsFoo.Chunks[0]); EXPECT_THAT(Error, tokensAre(S, "# error missing_foo")); EXPECT_EQ(Error.Kind, tok::pp_error); } @@ -114,14 +117,15 @@ TokenStream S = cook(lex(Code, Opts), Opts); DirectiveTree PP = DirectiveTree::parse(S); - ASSERT_THAT(PP.Chunks, ElementsAre(chunkKind(Chunk::K_Code), - chunkKind(Chunk::K_Directive), - chunkKind(Chunk::K_Code))); - EXPECT_THAT((const DirectiveTree::Code &)PP.Chunks[0], tokensAre(S, "/*A*/")); - const DirectiveTree::Directive &Define(PP.Chunks[1]); + ASSERT_THAT(PP.Chunks, + ElementsAre(codeChunk(), directiveChunk(), codeChunk())); + EXPECT_THAT(std::get(PP.Chunks[0]), + tokensAre(S, "/*A*/")); + const auto &Define = std::get(PP.Chunks[1]); EXPECT_EQ(Define.Kind, tok::pp_define); EXPECT_THAT(Define, tokensAre(S, "# /*B*/ /*C*/ define BAR /*D*/")); - EXPECT_THAT((const DirectiveTree::Code &)PP.Chunks[2], tokensAre(S, "/*E*/")); + EXPECT_THAT(std::get(PP.Chunks[2]), + tokensAre(S, "/*E*/")); } TEST(DirectiveTree, ParseBroken) { @@ -135,20 +139,18 @@ TokenStream S = cook(lex(Code, Opts), Opts); DirectiveTree PP = DirectiveTree::parse(S); - ASSERT_THAT(PP.Chunks, ElementsAre(chunkKind(Chunk::K_Code), - chunkKind(Chunk::K_Directive), - chunkKind(Chunk::K_Conditional))); - EXPECT_THAT((const DirectiveTree::Code &)PP.Chunks[0], tokensAre(S, "a")); - const DirectiveTree::Directive &Endif(PP.Chunks[1]); + ASSERT_THAT(PP.Chunks, + ElementsAre(codeChunk(), directiveChunk(), conditionalChunk())); + EXPECT_THAT(std::get(PP.Chunks[0]), tokensAre(S, "a")); + const auto &Endif = std::get(PP.Chunks[1]); EXPECT_EQ(Endif.Kind, tok::pp_endif); EXPECT_THAT(Endif, tokensAre(S, "# endif // mismatched")); - const DirectiveTree::Conditional &X(PP.Chunks[2]); + const auto &X = std::get(PP.Chunks[2]); EXPECT_EQ(1u, X.Branches.size()); // The (only) branch of the broken conditional section runs until eof. EXPECT_EQ(tok::pp_if, X.Branches.front().first.Kind); - EXPECT_THAT(X.Branches.front().second.Chunks, - ElementsAre(chunkKind(Chunk::K_Code))); + EXPECT_THAT(X.Branches.front().second.Chunks, ElementsAre(codeChunk())); // The missing terminating directive is marked as pp_not_keyword. EXPECT_EQ(tok::pp_not_keyword, X.End.Kind); EXPECT_EQ(0u, X.End.Tokens.size()); @@ -292,9 +294,10 @@ std::function Verify = [&](const DirectiveTree &M) { for (const auto &C : M.Chunks) { - if (C.kind() != DirectiveTree::Chunk::K_Conditional) + if (!std::holds_alternative(C)) continue; - const DirectiveTree::Conditional &Cond(C); + const DirectiveTree::Conditional &Cond = + std::get(C); for (unsigned I = 0; I < Cond.Branches.size(); ++I) { auto Directive = S.tokens(Cond.Branches[I].first.Tokens); EXPECT_EQ(I == Cond.Taken, Directive.back().text() == "// TAKEN") @@ -344,7 +347,7 @@ tokens("a a a b b b c c c e e e f f f h h h j j j")); const DirectiveTree &Part = - ((const DirectiveTree::Conditional &)Tree.Chunks[4]).Branches[0].second; + std::get(Tree.Chunks[4]).Branches[0].second; EXPECT_THAT(Part.stripDirectives(S).tokens(), tokens("c c c e e e f f f h h h")); }