[clang-tools-extra] bf0e219 - [pseudo] Use C++17 variant to simplify the DirectiveTree::Chunk class, NFC.
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 11 05:28:04 PDT 2022
Author: Haojian Wu
Date: 2022-08-11T14:27:38+02:00
New Revision: bf0e219d0481212ad12667262ed0b27e5e69f5f2
URL: https://github.com/llvm/llvm-project/commit/bf0e219d0481212ad12667262ed0b27e5e69f5f2
DIFF: https://github.com/llvm/llvm-project/commit/bf0e219d0481212ad12667262ed0b27e5e69f5f2.diff
LOG: [pseudo] Use C++17 variant to simplify the DirectiveTree::Chunk class, NFC.
Differential Revision: https://reviews.llvm.org/D131396
Added:
Modified:
clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h
clang-tools-extra/pseudo/lib/DirectiveTree.cpp
clang-tools-extra/pseudo/unittests/DirectiveTreeTest.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h b/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h
index e8220537649f9..2c6b1d2805697 100644
--- 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 <variant>
#include <vector>
namespace clang {
@@ -86,7 +87,7 @@ struct DirectiveTree {
};
/// Some piece of the file. {One of Code, Directive, Conditional}.
- class Chunk; // Defined below.
+ using Chunk = std::variant<Code, Directive, Conditional>;
std::vector<Chunk> Chunks;
/// Extract preprocessor structure by examining the raw tokens.
@@ -99,7 +100,6 @@ struct DirectiveTree {
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 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &,
/// The choices are stored in Conditional::Taken nodes.
void chooseConditionalBranches(DirectiveTree &, const TokenStream &Code);
-// FIXME: This approximates std::variant<Code, Directive, Conditional>.
-// 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<Code> CodeVariant;
- llvm::Optional<Directive> DirectiveVariant;
- llvm::Optional<Conditional> ConditionalVariant;
-};
-
} // namespace pseudo
} // namespace clang
diff --git a/clang-tools-extra/pseudo/lib/DirectiveTree.cpp b/clang-tools-extra/pseudo/lib/DirectiveTree.cpp
index d5f97455685ed..c9e0198227fa4 100644
--- 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 <variant>
namespace clang {
namespace pseudo {
@@ -141,6 +142,36 @@ class DirectiveParser {
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 @@ DirectiveTree DirectiveTree::parse(const TokenStream &Code) {
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 @@ class BranchChooser {
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 @@ class BranchChooser {
}
};
- 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 @@ class BranchChooser {
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 @@ class BranchChooser {
}
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<bool> isTakenWhenReached(const DirectiveTree::Directive &Dir) {
@@ -344,7 +314,7 @@ class BranchChooser {
} // namespace
void chooseConditionalBranches(DirectiveTree &Tree, const TokenStream &Code) {
- BranchChooser{Code}.choose(Tree);
+ BranchChooser{Code}.walk(Tree);
}
namespace {
@@ -358,31 +328,17 @@ class Preprocessor {
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
index f8732e28c5e1f..19e5e0526142a 100644
--- a/clang-tools-extra/pseudo/unittests/DirectiveTreeTest.cpp
+++ b/clang-tools-extra/pseudo/unittests/DirectiveTreeTest.cpp
@@ -44,7 +44,15 @@ MATCHER_P2(tokensAre, TS, Tokens, "tokens are " + std::string(Tokens)) {
return testing::Matches(tokens(Tokens))(TS.tokens(arg.Tokens));
}
-MATCHER_P(chunkKind, K, "") { return arg.kind() == K; }
+MATCHER(directiveChunk, "") {
+ return std::holds_alternative<DirectiveTree::Directive>(arg);
+}
+MATCHER(codeChunk, "") {
+ return std::holds_alternative<DirectiveTree::Code>(arg);
+}
+MATCHER(conditionalChunk, "") {
+ return std::holds_alternative<DirectiveTree::Conditional>(arg);
+}
TEST(DirectiveTree, Parse) {
LangOptions Opts;
@@ -66,19 +74,16 @@ TEST(DirectiveTree, Parse) {
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<DirectiveTree::Directive>(PP.Chunks[0]),
tokensAre(S, "# include < foo . h >"));
- EXPECT_THAT((const DirectiveTree::Code &)PP.Chunks[1],
+ EXPECT_THAT(std::get<DirectiveTree::Code>(PP.Chunks[1]),
tokensAre(S, "int main ( ) {"));
- EXPECT_THAT((const DirectiveTree::Code &)PP.Chunks[3], tokensAre(S, "}"));
+ EXPECT_THAT(std::get<DirectiveTree::Code>(PP.Chunks[3]), tokensAre(S, "}"));
- const DirectiveTree::Conditional &Ifdef(PP.Chunks[2]);
+ const auto &Ifdef = std::get<DirectiveTree::Conditional>(PP.Chunks[2]);
EXPECT_THAT(Ifdef.Branches,
ElementsAre(Pair(tokensAre(S, "# ifdef HAS_FOO"), _),
Pair(tokensAre(S, "# elif NEEDS_FOO"), _)));
@@ -87,17 +92,15 @@ TEST(DirectiveTree, Parse) {
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<DirectiveTree::Conditional>(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<DirectiveTree::Directive>(NeedsFoo.Chunks[0]);
EXPECT_THAT(Error, tokensAre(S, "# error missing_foo"));
EXPECT_EQ(Error.Kind, tok::pp_error);
}
@@ -114,14 +117,15 @@ BAR /*D*/
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<DirectiveTree::Code>(PP.Chunks[0]),
+ tokensAre(S, "/*A*/"));
+ const auto &Define = std::get<DirectiveTree::Directive>(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<DirectiveTree::Code>(PP.Chunks[2]),
+ tokensAre(S, "/*E*/"));
}
TEST(DirectiveTree, ParseBroken) {
@@ -135,20 +139,18 @@ TEST(DirectiveTree, ParseBroken) {
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<DirectiveTree::Code>(PP.Chunks[0]), tokensAre(S, "a"));
+ const auto &Endif = std::get<DirectiveTree::Directive>(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<DirectiveTree::Conditional>(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 @@ TEST(DirectiveTree, ChooseBranches) {
std::function<void(const DirectiveTree &)> Verify =
[&](const DirectiveTree &M) {
for (const auto &C : M.Chunks) {
- if (C.kind() != DirectiveTree::Chunk::K_Conditional)
+ if (!std::holds_alternative<DirectiveTree::Conditional>(C))
continue;
- const DirectiveTree::Conditional &Cond(C);
+ const DirectiveTree::Conditional &Cond =
+ std::get<DirectiveTree::Conditional>(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 @@ TEST(DirectiveTree, StripDirectives) {
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<DirectiveTree::Conditional>(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"));
}
More information about the cfe-commits
mailing list