[cfe-commits] [Patch][Review request] RecursiveASTVisitor can now traverse template specializations

Benoit Belley Benoit.Belley at autodesk.com
Wed Sep 1 11:59:34 PDT 2010


Hi John,

Here’s my latest patch. It should have addressed all of your issues. As far as I can tell, every instantiation of templated function (function template, member of a class template, specialization of classes, functions and members, partial specialization, etc...) is visited only once.


===================================================================
--- include/clang/AST/RecursiveASTVisitor.h	(revision 112699)
+++ include/clang/AST/RecursiveASTVisitor.h	(working copy)
@@ -123,12 +123,26 @@
 /// users may override Traverse* and WalkUpFrom* to implement custom
 /// traversal strategies.  Returning false from one of these overridden
 /// functions will abort the entire traversal.
+///
+/// This visitor always recurses in the declarator of explicit
+/// template instantiations, such as for example "template class
+/// set<int>'. But by default, it will not recurse in the
+/// instantiations (implicit or explicit) of the template (function or
+/// class) themselves since the instatiated AST isn't written in the
+/// source code anywhere. This behavior can be changed by overriding
+/// shouldVisitTemplateInstantiations() in the derived class to return
+/// true.
+
 template<typename Derived>
 class RecursiveASTVisitor {
 public:
   /// \brief Return a reference to the derived class.
   Derived &getDerived() { return *static_cast<Derived*>(this); }
 
+  /// \brief Return whether this visitor should recurse into
+  /// template instantiations.
+  bool shouldVisitTemplateInstantiations() const { return false; }
+  
   /// \brief Recursively visit a statement or expression, by
   /// dispatching to Traverse*() based on the argument's dynamic type.
   ///
@@ -351,7 +365,9 @@
 private:
   // These are helper methods used by more than one Traverse* method.
   bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
-  bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL,
+  bool TraverseImplicitClassInstantiations(ClassTemplateDecl* D);
+  bool TraverseFunctionInstantiations(FunctionTemplateDecl* D) ;
+  bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL, 
                                           unsigned Count);
   bool TraverseArrayTypeLocHelper(ArrayTypeLoc TL);
   bool TraverseRecordHelper(RecordDecl *D);
@@ -1092,19 +1108,124 @@
   return true;
 }
 
+// A helper method for traversing the implicit instantiations of a
+// class.
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseImplicitClassInstantiations(
+  ClassTemplateDecl* D) {
+  // By default, we do not traverse the instantiations of
+  // class templates since they do not apprear in the user code. The
+  // following code optionally traverses them.
+  if (!getDerived().shouldVisitTemplateInstantiations())
+    return true;
+
+  ClassTemplateDecl::spec_iterator end = D->spec_end();
+  for (ClassTemplateDecl::spec_iterator it = D->spec_begin(); it != end; ++it) {
+    ClassTemplateSpecializationDecl* SD = *it;
+
+    switch (SD->getSpecializationKind()) {
+    case TSK_ImplicitInstantiation:
+      TRY_TO(TraverseClassTemplateSpecializationDecl(SD));
+      break;
+    case TSK_Undeclared:
+    case TSK_ExplicitInstantiationDeclaration:
+    case TSK_ExplicitInstantiationDefinition:
+    case TSK_ExplicitSpecialization:
+      break;
+    default:
+      assert(false && "Unknown specialization kind.");
+    }
+  }
+
+  ClassTemplateDecl::partial_spec_iterator pend = D->partial_spec_end();
+  for (ClassTemplateDecl::partial_spec_iterator it = D->partial_spec_begin(); it != pend; ++it) {
+    ClassTemplatePartialSpecializationDecl* SD = *it;
+
+    switch (SD->getSpecializationKind()) {
+    case TSK_ImplicitInstantiation:
+      TRY_TO(TraverseClassTemplateSpecializationDecl(SD));
+      break;
+    case TSK_Undeclared:
+    case TSK_ExplicitInstantiationDeclaration:
+    case TSK_ExplicitInstantiationDefinition:
+    case TSK_ExplicitSpecialization:
+      break;
+    default:
+      assert(false && "Unknown specialization kind.");
+    }
+  }
+
+  return true;
+}
+
 DEF_TRAVERSE_DECL(ClassTemplateDecl, {
-    TRY_TO(TraverseDecl(D->getTemplatedDecl()));
+    CXXRecordDecl* TempDecl = D->getTemplatedDecl();
+    TRY_TO(TraverseDecl(TempDecl));
     TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
-    // We should not traverse the specializations/partial
-    // specializations.  Those will show up in other contexts.
-    // getInstantiatedFromMemberTemplate() is just a link from a
-    // template instantiation back to the template from which it was
-    // instantiated, and thus should not be traversed either.
+    // Explicit class intantiations and specializations will be
+    // traversed from the context of their declaration. There
+    // is therefore no need to traverse them for here.
+    //
+    // In addition, we only traverse the class instantiations when the
+    // class template is a class template definition or the canonical
+    // declaration when no definition of the class exists.
+    if (TempDecl) {
+      CXXRecordDecl* DefDecl  = TempDecl->getDefinition();
+      CXXRecordDecl* CanoDecl = TempDecl->getCanonicalDecl();
+      if (TempDecl == DefDecl || (!DefDecl && TempDecl == CanoDecl)) {
+        TRY_TO(TraverseImplicitClassInstantiations(D));
+      }
+    }
+
+    // Note that getInstantiatedFromMemberTemplate() is just a link
+    // from a template instantiation back to the template from which
+    // it was instantiated, and thus should not be traversed.
   })
 
+// A helper method for traversing the instantiations of a
+// function while skipping its specializations.
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseFunctionInstantiations(
+  FunctionTemplateDecl* D) {
+  // By default, we do not traverse the instantiations of
+  // function templates since they do not apprear in the user code. The
+  // following code optionally traverses them.
+  if (!getDerived().shouldVisitTemplateInstantiations())
+    return true;
+  
+  FunctionTemplateDecl::spec_iterator end = D->spec_end();
+  for (FunctionTemplateDecl::spec_iterator it = D->spec_begin(); it != end; ++it) {
+    FunctionDecl* FD = *it;
+    switch (FD->getTemplateSpecializationKind()) {
+    case TSK_ImplicitInstantiation: 
+    case TSK_ExplicitInstantiationDeclaration:
+    case TSK_ExplicitInstantiationDefinition:
+      TRY_TO(TraverseFunctionDecl(FD));
+      break;
+
+    case TSK_Undeclared:           // Declaration of the template definition.
+    case TSK_ExplicitSpecialization:
+      break;
+    default:
+      assert(false && "Unknown specialization kind.");
+    }
+  }
+  
+  return true;
+}
+  
 DEF_TRAVERSE_DECL(FunctionTemplateDecl, {
     TRY_TO(TraverseDecl(D->getTemplatedDecl()));
     TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+    // Explicit function specializations will be traversed from the
+    // context of their declaration. There is therefore no need to
+    // traverse them for here.
+    //
+    // In addition, we only traverse the function instantiations when
+    // the function template is a function template definition.
+    if (D->isThisDeclarationADefinition()) {
+      TRY_TO(TraverseFunctionInstantiations(D));
+    }
   })
 
 DEF_TRAVERSE_DECL(TemplateTemplateParmDecl, {
@@ -1200,10 +1321,16 @@
     // ("template set<int>;"), we do need a callback, since this
     // is the only callback that's made for this instantiation.
     // We use getTypeAsWritten() to distinguish.
-    // FIXME: see how we want to handle template specializations.
     if (TypeSourceInfo *TSI = D->getTypeAsWritten())
       TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
-    return true;
+
+    if (!getDerived().shouldVisitTemplateInstantiations() &&
+        D->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+      // Returning from here skips traversing the
+      // declaration context of the ClassTemplateSpecializationDecl
+      // (embedded in the DEF_TRAVERSE_DECL() macro)
+      // which contains the instantiated members of the class.
+      return true;
   })
 
 template <typename Derived>


Benoit


-------------- next part --------------
A non-text attachment was scrubbed...
Name: RecursiveASTVisitor-TemplSpec.patch
Type: application/octet-stream
Size: 8114 bytes
Desc: RecursiveASTVisitor-TemplSpec.patch
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20100901/dad4357b/attachment.obj>
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: ATT00001..txt
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20100901/dad4357b/attachment.txt>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: image002.gif
Type: image/gif
Size: 651 bytes
Desc: image002.gif
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20100901/dad4357b/attachment.gif>
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: ATT00002..txt
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20100901/dad4357b/attachment-0001.txt>


More information about the cfe-commits mailing list