[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