6

Say I have a variable declaration like this:

std::vector<MyType> myVector(1);

This is represented as a CXXConstructExpr in the Clang AST. I have a matcher that finds this CXXConstructExpr, but I'd like to extract the decl for MyType from it.

I've tried all sorts of things, but nothing seems to work:

const CXXConstructExpr* construct = Result.Nodes.getNodeAs<CXXConstructExpr>("expr");
construct->getConstructor()->getTemplateSpecializationArgs()  // Always nullptr
construct->getConstructor()->getParent()  // Seems to lose the template parameters
construct->getConstructor()->getDescribedTemplate()  // Always nullptr

1 Answer 1

2
+200

Here's a matcher:

varDecl(
  has(
    cxxConstructExpr()
  )
 ,hasType(
    classTemplateSpecializationDecl().bind(sp_dcl_bd_name_)
  )
).bind(var_bd_name_);

It starts with the VarDecl and traverses to the type, which is a ClassTemplateSpecializationDecl buried in vector's ClassTemplateDecl. In the callback, one can work from the ClassTemplateSpecializationDecl to the template argument list, and operate on the individual template arguments:

using CTSD = ClassTemplateSpecializationDecl;
CTSD * spec_decl =
    const_cast<CTSD *>(result.Nodes.getNodeAs<CTSD>(sp_dcl_bd_name_));
VarDecl * var_decl =
    const_cast<VarDecl *>(result.Nodes.getNodeAs<VarDecl>(var_bd_name_));
if(spec_decl && var_decl) {
  // get the template args
  TemplateArgumentList const &tal(spec_decl->getTemplateArgs());
  for(unsigned i = 0; i < tal.size(); ++i){
    TemplateArgument const &ta(tal[i]);
    // is this arg a type arg? If so, get that type
    TemplateArgument::ArgKind k(ta.getKind());
    std::string argName = "";
    if(k==TemplateArgument::ArgKind::Type){
      QualType t = ta.getAsType();
      argName = t.getAsString();
    }
    // Could do similar actions for integral args, etc...
    std::cout << "For variable declared at "
      << corct::sourceRangeAsString(var_decl->getSourceRange(),&sm) << ":"
      << spec_decl->getNameAsString()
      << ": template arg " << (i+1) << ": " << argName << std::endl;
  } // for template args
} // if

For this code:

struct B{int b_;};
std::vector<B> vb(1);

this produces:

For variable declared at <line:14:1, col:20>:vector: template arg 1: struct B
For variable declared at <col:1, col:20>:vector: template arg 2: class std::__1::allocator<struct B>

The full example is in the Code Analysis and Refactoring with Clang Tools examples repo at github: https://github.com/lanl/CoARCT (see apps/TemplateType.cc)

Sign up to request clarification or add additional context in comments.

1 Comment

I had to use hasDescendant() instead of has() because the CXXConstructExpr isn't always a direct child of the VarDecl, but this otherwise worked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.