[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