[prev in list] [next in list] [prev in thread] [next in thread] 

List:       cfe-commits
Subject:    [PATCH] D159309: [ASTMatchers] basic matcher support for ConceptReference
From:       Sam McCall via Phabricator via cfe-commits <cfe-commits () lists ! llvm ! org>
Date:       2023-08-31 20:51:46
Message-ID: 3yW68FAVQKCR6ELM120hNg () geopod-ismtpd-10
[Download RAW message or body]

sammccall created this revision.
sammccall added reviewers: massberg, aaron.ballman.
Herald added a project: All.
sammccall requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

This adds only the trivial conceptReference() matcher.
(Also adds tests for ConceptReference in DynTypedNode, which need this
matcher)

This node isn't part of any other inheritance hierarchy, so it meant
handling a new base type everywhere.

This caused us to run out of discriminator bits in the CurMatchData,
so I replaced the data structure with std::variant which is at least
less code.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D159309

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchFinder.h
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/include/clang/ASTMatchers/ASTMatchersInternal.h
  clang/lib/ASTMatchers/ASTMatchFinder.cpp
  clang/lib/ASTMatchers/ASTMatchersInternal.cpp
  clang/lib/ASTMatchers/Dynamic/Registry.cpp
  clang/unittests/AST/ASTTypeTraitsTest.cpp
  clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp


["D159309.555159.patch" (D159309.555159.patch)]

Index: clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===================================================================
--- clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -770,6 +770,17 @@
                       cxxCtorInitializer(forField(hasName("i")))));
 }
 
+TEST_P(ASTMatchersTest, Match_ConceptReference) {
+  if (!GetParam().isCXX20OrLater()) {
+    return;
+  }
+  std::string Concept = "template <class> concept C = true;\n";
+  EXPECT_TRUE(matches(Concept + "auto X = C<int>;", conceptReference()));
+  EXPECT_TRUE(matches(Concept + "C auto X = 0;", conceptReference()));
+  EXPECT_TRUE(matches(Concept + "template <C X> int i;", conceptReference()));
+  EXPECT_TRUE(matches(Concept + "void foo(C auto X) {}", conceptReference()));
+}
+
 TEST_P(ASTMatchersTest, Matcher_ThisExpr) {
   if (!GetParam().isCXX()) {
     return;
Index: clang/unittests/AST/ASTTypeTraitsTest.cpp
===================================================================
--- clang/unittests/AST/ASTTypeTraitsTest.cpp
+++ clang/unittests/AST/ASTTypeTraitsTest.cpp
@@ -210,7 +210,13 @@
                              ast_matchers::attr()));
 }
 
-// FIXME: add tests for ConceptReference once we add an ASTMatcher.
+TEST(DynTypedNode, ConceptReferenceSourceRange) {
+  RangeVerifier<ConceptReference> Verifier;
+  Verifier.expectRange(2, 10, 2, 15);
+  EXPECT_TRUE(Verifier.match("template <class> concept C = true;\n"
+                             "auto X = C<int>;",
+                             conceptReference()));
+}
 
 TEST(DynTypedNode, DeclDump) {
   DumpVerifier Verifier;
@@ -224,6 +230,14 @@
   EXPECT_TRUE(Verifier.match("void f() {}", stmt()));
 }
 
+TEST(DynTypedNode, ConceptReferenceDump) {
+  DumpVerifier Verifier;
+  Verifier.expectSubstring("ConceptReference");
+  EXPECT_TRUE(Verifier.match("template <class> concept C = true;\n"
+                             "auto X = C<int>;",
+                             conceptReference()));
+}
+
 TEST(DynTypedNode, DeclPrint) {
   PrintVerifier Verifier;
   Verifier.expectString("void f() {\n}\n");
@@ -236,6 +250,14 @@
   EXPECT_TRUE(Verifier.match("void f() {}", stmt()));
 }
 
+TEST(DynTypedNode, ConceptReferencePrint) {
+  PrintVerifier Verifier;
+  Verifier.expectString("C<int>");
+  EXPECT_TRUE(Verifier.match("template <class> concept C = true;\n"
+                             "auto X = C<int>;",
+                             conceptReference()));
+}
+
 TEST(DynTypedNode, QualType) {
   QualType Q;
   DynTypedNode Node = DynTypedNode::create(Q);
Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp
===================================================================
--- clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -173,6 +173,7 @@
   REGISTER_MATCHER(compoundStmt);
   REGISTER_MATCHER(coawaitExpr);
   REGISTER_MATCHER(conceptDecl);
+  REGISTER_MATCHER(conceptReference);
   REGISTER_MATCHER(conditionalOperator);
   REGISTER_MATCHER(constantArrayType);
   REGISTER_MATCHER(constantExpr);
Index: clang/lib/ASTMatchers/ASTMatchersInternal.cpp
===================================================================
--- clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -768,6 +768,7 @@
     templateTypeParmDecl;
 const internal::VariadicDynCastAllOfMatcher<Decl, TemplateTemplateParmDecl>
     templateTemplateParmDecl;
+const internal::VariadicAllOfMatcher<ConceptReference> conceptReference;
 
 const internal::VariadicAllOfMatcher<LambdaCapture> lambdaCapture;
 const internal::VariadicAllOfMatcher<QualType> qualType;
Index: clang/lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -26,6 +26,7 @@
 #include <deque>
 #include <memory>
 #include <set>
+#include <variant>
 
 namespace clang {
 namespace ast_matchers {
@@ -136,6 +137,8 @@
       traverse(*TALoc);
     else if (const Attr *A = DynNode.get<Attr>())
       traverse(*A);
+    else if (const ConceptReference *CR = DynNode.get<ConceptReference>())
+      traverse(*CR);
     // FIXME: Add other base types after adding tests.
 
     // It's OK to always overwrite the bound nodes, as if there was
@@ -275,6 +278,12 @@
     ScopedIncrement ScopedDepth(&CurrentDepth);
     return traverse(*A);
   }
+  bool TraverseConceptReference(ConceptReference *CR) {
+    if (CR == nullptr)
+      return true;
+    ScopedIncrement ScopedDepth(&CurrentDepth);
+    return traverse(*CR);
+  }
   bool TraverseLambdaExpr(LambdaExpr *Node) {
     if (!Finder->isTraversalIgnoringImplicitNodes())
       return VisitorBase::TraverseLambdaExpr(Node);
@@ -360,6 +369,10 @@
   bool baseTraverse(const Attr &AttrNode) {
     return VisitorBase::TraverseAttr(const_cast<Attr *>(&AttrNode));
   }
+  bool baseTraverse(const ConceptReference &CR) {
+    return VisitorBase::TraverseConceptReference(
+        const_cast<ConceptReference *>(&CR));
+  }
 
   // Sets 'Matched' to true if 'Matcher' matches 'Node' and:
   //   0 < CurrentDepth <= MaxDepth.
@@ -505,6 +518,7 @@
   bool TraverseConstructorInitializer(CXXCtorInitializer *CtorInit);
   bool TraverseTemplateArgumentLoc(TemplateArgumentLoc TAL);
   bool TraverseAttr(Attr *AttrNode);
+  bool TraverseConceptReference(ConceptReference *);
 
   bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue) {
     if (auto *RF = dyn_cast<CXXForRangeStmt>(S)) {
@@ -712,6 +726,8 @@
       match(*N);
     } else if (auto *N = Node.get<Attr>()) {
       match(*N);
+    } else if (auto *N = Node.get<ConceptReference>()) {
+      match(*N);
     }
   }
 
@@ -766,85 +782,14 @@
   bool TraversingASTNodeNotAsIs = false;
   bool TraversingASTChildrenNotSpelledInSource = false;
 
-  class CurMatchData {
-// We don't have enough free low bits in 32bit builds to discriminate 8 pointer
-// types in PointerUnion. so split the union in 2 using a free bit from the
-// callback pointer.
-#define CMD_TYPES_0                                                            \
-  const QualType *, const TypeLoc *, const NestedNameSpecifier *,              \
-      const NestedNameSpecifierLoc *
-#define CMD_TYPES_1                                                            \
-  const CXXCtorInitializer *, const TemplateArgumentLoc *, const Attr *,       \
-      const DynTypedNode *
-
-#define IMPL(Index)                                                            \
-  template <typename NodeType>                                                 \
-  std::enable_if_t<                                                            \
-      llvm::is_one_of<const NodeType *, CMD_TYPES_##Index>::value>             \
-  SetCallbackAndRawNode(const MatchCallback *CB, const NodeType &N) {          \
-    assertEmpty();                                                             \
-    Callback.setPointerAndInt(CB, Index);                                      \
-    Node##Index = &N;                                                          \
-  }                                                                            \
-                                                                               \
-  template <typename T>                                                        \
-  std::enable_if_t<llvm::is_one_of<const T *, CMD_TYPES_##Index>::value,       \
-                   const T *>                                                  \
-  getNode() const {                                                            \
-    assertHoldsState();                                                        \
-    return Callback.getInt() == (Index) ? Node##Index.dyn_cast<const T *>()    \
-                                        : nullptr;                             \
-  }
-
-  public:
-    CurMatchData() : Node0(nullptr) {}
-
-    IMPL(0)
-    IMPL(1)
-
-    const MatchCallback *getCallback() const { return Callback.getPointer(); }
-
-    void SetBoundNodes(const BoundNodes &BN) {
-      assertHoldsState();
-      BNodes = &BN;
-    }
-
-    void clearBoundNodes() {
-      assertHoldsState();
-      BNodes = nullptr;
-    }
-
-    const BoundNodes *getBoundNodes() const {
-      assertHoldsState();
-      return BNodes;
-    }
-
-    void reset() {
-      assertHoldsState();
-      Callback.setPointerAndInt(nullptr, 0);
-      Node0 = nullptr;
-    }
-
-  private:
-    void assertHoldsState() const {
-      assert(Callback.getPointer() != nullptr && !Node0.isNull());
-    }
-
-    void assertEmpty() const {
-      assert(Callback.getPointer() == nullptr && Node0.isNull() &&
-             BNodes == nullptr);
-    }
-
-    llvm::PointerIntPair<const MatchCallback *, 1> Callback;
-    union {
-      llvm::PointerUnion<CMD_TYPES_0> Node0;
-      llvm::PointerUnion<CMD_TYPES_1> Node1;
-    };
+  struct CurMatchData {
+    const MatchCallback *Callback = nullptr;
     const BoundNodes *BNodes = nullptr;
-
-#undef CMD_TYPES_0
-#undef CMD_TYPES_1
-#undef IMPL
+    std::variant<std::monostate, const QualType *, const TypeLoc *,
+                 const NestedNameSpecifier *, const NestedNameSpecifierLoc *,
+                 const CXXCtorInitializer *, const TemplateArgumentLoc *,
+                 const Attr *, const DynTypedNode *, const ConceptReference *>
+        Node;
   } CurMatchState;
 
   struct CurMatchRAII {
@@ -852,10 +797,17 @@
     CurMatchRAII(MatchASTVisitor &MV, const MatchCallback *CB,
                  const NodeType &NT)
         : MV(MV) {
-      MV.CurMatchState.SetCallbackAndRawNode(CB, NT);
+      assert(MV.CurMatchState.Callback == nullptr &&
+             std::holds_alternative<std::monostate>(MV.CurMatchState.Node) &&
+             MV.CurMatchState.BNodes == nullptr);
+      MV.CurMatchState.Callback = CB;
+      MV.CurMatchState.Node = &NT;
     }
 
-    ~CurMatchRAII() { MV.CurMatchState.reset(); }
+    ~CurMatchRAII() {
+      MV.CurMatchState.Callback = nullptr;
+      MV.CurMatchState.Node = std::monostate{};
+    }
 
   private:
     MatchASTVisitor &MV;
@@ -890,30 +842,22 @@
 
     static void dumpNodeFromState(const ASTContext &Ctx,
                                   const CurMatchData &State, raw_ostream &OS) {
-      if (const DynTypedNode *MatchNode = State.getNode<DynTypedNode>()) {
-        dumpNode(Ctx, *MatchNode, OS);
-      } else if (const auto *QT = State.getNode<QualType>()) {
-        dumpNode(Ctx, DynTypedNode::create(*QT), OS);
-      } else if (const auto *TL = State.getNode<TypeLoc>()) {
-        dumpNode(Ctx, DynTypedNode::create(*TL), OS);
-      } else if (const auto *NNS = State.getNode<NestedNameSpecifier>()) {
-        dumpNode(Ctx, DynTypedNode::create(*NNS), OS);
-      } else if (const auto *NNSL = State.getNode<NestedNameSpecifierLoc>()) {
-        dumpNode(Ctx, DynTypedNode::create(*NNSL), OS);
-      } else if (const auto *CtorInit = State.getNode<CXXCtorInitializer>()) {
-        dumpNode(Ctx, DynTypedNode::create(*CtorInit), OS);
-      } else if (const auto *TAL = State.getNode<TemplateArgumentLoc>()) {
-        dumpNode(Ctx, DynTypedNode::create(*TAL), OS);
-      } else if (const auto *At = State.getNode<Attr>()) {
-        dumpNode(Ctx, DynTypedNode::create(*At), OS);
-      }
+      std::visit(llvm::makeVisitor([&](std::monostate) {},
+                                   [&](const DynTypedNode *Node) {
+                                     dumpNode(Ctx, *Node, OS);
+                                   },
+                                   [&](const auto *Node) {
+                                     dumpNode(Ctx, DynTypedNode::create(*Node),
+                                              OS);
+                                   }),
+                 State.Node);
     }
 
   public:
     TraceReporter(const MatchASTVisitor &MV) : MV(MV) {}
     void print(raw_ostream &OS) const override {
       const CurMatchData &State = MV.CurMatchState;
-      const MatchCallback *CB = State.getCallback();
+      const MatchCallback *CB = State.Callback;
       if (!CB) {
         OS << "ASTMatcher: Not currently matching\n";
         return;
@@ -924,7 +868,7 @@
 
       ASTContext &Ctx = MV.getASTContext();
 
-      if (const BoundNodes *Nodes = State.getBoundNodes()) {
+      if (const BoundNodes *Nodes = State.BNodes) {
         OS << "ASTMatcher: Processing '" << CB->getID() << "' against:\n\t";
         dumpNodeFromState(Ctx, State, OS);
         const BoundNodes::IDToNodeMap &Map = Nodes->getMap();
@@ -1102,6 +1046,9 @@
   void matchDispatch(const Attr *Node) {
     matchWithoutFilter(*Node, Matchers->Attr);
   }
+  void matchDispatch(const ConceptReference *Node) {
+    matchWithoutFilter(*Node, Matchers->ConceptReference);
+  }
   void matchDispatch(const void *) { /* Do nothing. */ }
   /// @}
 
@@ -1240,10 +1187,11 @@
     struct CurBoundScope {
       CurBoundScope(MatchASTVisitor::CurMatchData &State, const BoundNodes &BN)
           : State(State) {
-        State.SetBoundNodes(BN);
+        assert(State.BNodes == nullptr);
+        State.BNodes = &BN;
       }
 
-      ~CurBoundScope() { State.clearBoundNodes(); }
+      ~CurBoundScope() { State.BNodes = nullptr; }
 
     private:
       MatchASTVisitor::CurMatchData &State;
@@ -1526,6 +1474,11 @@
   return RecursiveASTVisitor<MatchASTVisitor>::TraverseAttr(AttrNode);
 }
 
+bool MatchASTVisitor::TraverseConceptReference(ConceptReference *CR) {
+  match(*CR);
+  return RecursiveASTVisitor::TraverseConceptReference(CR);
+}
+
 class MatchASTConsumer : public ASTConsumer {
 public:
   MatchASTConsumer(MatchFinder *Finder,
@@ -1626,6 +1579,12 @@
   Matchers.AllCallbacks.insert(Action);
 }
 
+void MatchFinder::addMatcher(const ConceptReferenceMatcher &AttrMatch,
+                             MatchCallback *Action) {
+  Matchers.ConceptReference.emplace_back(AttrMatch, Action);
+  Matchers.AllCallbacks.insert(Action);
+}
+
 bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch,
                                     MatchCallback *Action) {
   if (NodeMatch.canConvertTo<Decl>()) {
@@ -1655,6 +1614,9 @@
   } else if (NodeMatch.canConvertTo<Attr>()) {
     addMatcher(NodeMatch.convertTo<Attr>(), Action);
     return true;
+  } else if (NodeMatch.canConvertTo<ConceptReference>()) {
+    addMatcher(NodeMatch.convertTo<ConceptReference>(), Action);
+    return true;
   }
   return false;
 }
Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -1163,19 +1163,10 @@
 /// IsBaseType<T>::value is true if T is a "base" type in the AST
 /// node class hierarchies.
 template <typename T>
-struct IsBaseType {
-  static const bool value =
-      std::is_same<T, Decl>::value || std::is_same<T, Stmt>::value ||
-      std::is_same<T, QualType>::value || std::is_same<T, Type>::value ||
-      std::is_same<T, TypeLoc>::value ||
-      std::is_same<T, NestedNameSpecifier>::value ||
-      std::is_same<T, NestedNameSpecifierLoc>::value ||
-      std::is_same<T, CXXCtorInitializer>::value ||
-      std::is_same<T, TemplateArgumentLoc>::value ||
-      std::is_same<T, Attr>::value;
-};
-template <typename T>
-const bool IsBaseType<T>::value;
+using IsBaseType =
+    llvm::is_one_of<T, Decl, Stmt, QualType, Type, TypeLoc, NestedNameSpecifier,
+                    NestedNameSpecifierLoc, CXXCtorInitializer,
+                    TemplateArgumentLoc, Attr, ConceptReference>;
 
 /// A "type list" that contains all types.
 ///
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -152,6 +152,7 @@
 using TemplateArgumentLocMatcher = internal::Matcher<TemplateArgumentLoc>;
 using LambdaCaptureMatcher = internal::Matcher<LambdaCapture>;
 using AttrMatcher = internal::Matcher<Attr>;
+using ConceptReferenceMatcher = internal::Matcher<ConceptReference>;
 /// @}
 
 /// Matches any node.
@@ -611,6 +612,18 @@
                                                    TemplateTemplateParmDecl>
     templateTemplateParmDecl;
 
+/// Matches concept references.
+///
+/// Given
+/// \code
+///   template <class> concept C = true;
+///   bool X = C<int>;
+/// \endcode
+/// conceptReference()
+///   matches 'C<int>'.
+extern const internal::VariadicAllOfMatcher<ConceptReference> conceptReference;
+
+
 /// Matches public C++ declarations and C++ base specifers that specify public
 /// inheritance.
 ///
Index: clang/include/clang/ASTMatchers/ASTMatchFinder.h
===================================================================
--- clang/include/clang/ASTMatchers/ASTMatchFinder.h
+++ clang/include/clang/ASTMatchers/ASTMatchFinder.h
@@ -169,6 +169,8 @@
   void addMatcher(const TemplateArgumentLocMatcher &NodeMatch,
                   MatchCallback *Action);
   void addMatcher(const AttrMatcher &NodeMatch, MatchCallback *Action);
+  void addMatcher(const ConceptReferenceMatcher &NodeMatch,
+                  MatchCallback *Action);
   /// @}
 
   /// Adds a matcher to execute when running over the AST.
@@ -222,6 +224,8 @@
     std::vector<std::pair<TemplateArgumentLocMatcher, MatchCallback *>>
         TemplateArgumentLoc;
     std::vector<std::pair<AttrMatcher, MatchCallback *>> Attr;
+    std::vector<std::pair<ConceptReferenceMatcher, MatchCallback *>>
+        ConceptReference;
     /// All the callbacks in one container to simplify iteration.
     llvm::SmallPtrSet<MatchCallback *, 16> AllCallbacks;
   };
Index: clang/docs/LibASTMatchersReference.html
===================================================================
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -611,6 +611,17 @@
 </pre></td></tr>
 
 
+<tr><td>Matcher&lt;<a \
href="https://clang.llvm.org/doxygen/classclang_1_1ConceptReference.html">ConceptReference</a>&gt;</td><td \
class="name" onclick="toggle('conceptReference0')"><a \
name="conceptReference0Anchor">conceptReference</a></td><td>Matcher&lt;<a \
href="https://clang.llvm.org/doxygen/classclang_1_1ConceptReference.html">ConceptReference</a>&gt;...</td></tr>
 +<tr><td colspan="4" class="doc" id="conceptReference0"><pre>Matches concept \
references. +
+Given
+  template &lt;class&gt; concept C = true;
+  bool X = C&lt;int&gt;;
+conceptReference()
+  matches 'C&lt;int&gt;'.
+</pre></td></tr>
+
+
 <tr><td>Matcher&lt;<a \
href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt;</td><td \
class="name" onclick="toggle('accessSpecDecl0')"><a \
name="accessSpecDecl0Anchor">accessSpecDecl</a></td><td>Matcher&lt;<a \
href="https://clang.llvm.org/doxygen/classclang_1_1AccessSpecDecl.html">AccessSpecDecl</a>&gt;...</td></tr>
  <tr><td colspan="4" class="doc" id="accessSpecDecl0"><pre>Matches C++ access \
specifier declarations.  
@@ -2531,20 +2542,21 @@
   class array {
     T data[Size];
   };
-dependentSizedArrayType()
+dependentSizedArrayType
   matches "T data[Size]"
 </pre></td></tr>
 
 
 <tr><td>Matcher&lt;<a \
href="https://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>&gt;</td><td \
class="name" onclick="toggle('dependentSizedExtVectorType0')"><a \
name="dependentSizedExtVectorType0Anchor">dependentSizedExtVectorType</a></td><td>Matcher&lt;<a \
href="https://clang.llvm.org/doxygen/classclang_1_1DependentSizedExtVectorType.html">DependentSizedExtVectorType</a>&gt;...</td></tr>
                
-<tr><td colspan="4" class="doc" id="dependentSizedExtVectorType0"><pre>Matches C++ \
extended vector type where either the type or size is dependent. +<tr><td colspan="4" \
class="doc" id="dependentSizedExtVectorType0"><pre>Matches C++ extended vector type \
where either the type or size is +dependent.
 
 Given
   template&lt;typename T, int Size&gt;
   class vector {
     typedef T __attribute__((ext_vector_type(Size))) type;
   };
-dependentSizedExtVectorType()
+dependentSizedExtVectorType
   matches "T __attribute__((ext_vector_type(Size)))"
 </pre></td></tr>
 


[Attachment #4 (text/plain)]

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic