RecursiveASTVisitor not traversing implicit ParmVarDecl's in class instantiation methods?

Hi,

I have this example code:

template <class T>
class D {
  public:
    static void f(int c) {int b = c;}
};
int main(){ D<char>::f(0); }

and a clang plugin using RecursiveASTVisitor:

class TestConsumer : public ASTConsumer, public RecursiveASTVisitor<TestConsumer> {
public:
  virtual void HandleTranslationUnit(ASTContext &ctx) { TraverseDecl(ctx.getTranslationUnitDecl()); }
  bool shouldVisitTemplateInstantiations() const { return true; }
  std::string QName(const NamedDecl *d) {
    std::string s;
    llvm::raw_string_ostream os(s);
    d->getNameForDiagnostic(os, d->getASTContext().getPrintingPolicy(), true);
    os.flush();
    if (d->getDeclContext()->isFunctionOrMethod())
      s.insert(0, QName(dyn_cast<NamedDecl>(d->getDeclContext())) + "::");
    return s;
  }
  bool VisitDecl(Decl *d) {
    std::cout << "\n" << d->getDeclKindName() << " decl: " << d << "\n";
    const NamedDecl *nd = dyn_cast<NamedDecl>(d);
    if (nd)
      std::cout << "Qualified name: " << QName(nd) << "\n";
    return true;
  }
  bool VisitDeclRefExpr(DeclRefExpr *e) {
    std::cout << "\nReferenced decl: " << QName(e->getDecl()) << " - " << e->getDecl() << "\n";
    return true;
  }
};

Note that shouldVisitTemplateInstantiations() is set to return true. The issue is that the ParmVarDecl for D<char>::f::c is never visited, while the one for D::f::c is visited twice. As a result, VisitDeclRefExpr visits an expression whose Decl is never visited and I can't bind it :frowning:

Since D<char>::f::c is present in ast-dump output as a distinct ParmVarDecl node, I guess it should be visited. To do it in a non-hacky and generic way, I guess I need to fix some Traverse* method in RecursiveASTVisitor.h, but I'm a bit clueless as to what goes wrong. I've checked TraverseFunctionHelper and its parameter handling, but can't find anything wrong at a glance. Halp, plz? Any tips appreciated, I'm a rookie.

Hi,

I have this example code:

>>>>>>>>>>>>>>>>>>
template <class T>
class D {
  public:
    static void f(int c) {int b = c;}
};
int main(){ D<char>::f(0); }
<<<<<<<<<<<<<<<<<<

and a clang plugin using RecursiveASTVisitor:

>>>>>>>>>>>>>>>>>>
class TestConsumer : public ASTConsumer, public
RecursiveASTVisitor<TestConsumer> {
public:
  virtual void HandleTranslationUnit(ASTContext &ctx) {
TraverseDecl(ctx.getTranslationUnitDecl()); }
  bool shouldVisitTemplateInstantiations() const { return true; }
  std::string QName(const NamedDecl *d) {
    std::string s;
    llvm::raw_string_ostream os(s);
    d->getNameForDiagnostic(os, d->getASTContext().getPrintingPolicy(),
true);
    os.flush();
    if (d->getDeclContext()->isFunctionOrMethod())
      s.insert(0, QName(dyn_cast<NamedDecl>(d->getDeclContext())) + "::");
    return s;
  }
  bool VisitDecl(Decl *d) {
    std::cout << "\n" << d->getDeclKindName() << " decl: " << d << "\n";
    const NamedDecl *nd = dyn_cast<NamedDecl>(d);
    if (nd)
      std::cout << "Qualified name: " << QName(nd) << "\n";
    return true;
  }
  bool VisitDeclRefExpr(DeclRefExpr *e) {
    std::cout << "\nReferenced decl: " << QName(e->getDecl()) << " - " <<
e->getDecl() << "\n";
    return true;
  }
};
<<<<<<<<<<<<<<<<<<

Note that shouldVisitTemplateInstantiations() is set to return true. The
issue is that the ParmVarDecl for D<char>::f::c is never visited, while the
one for D::f::c is visited twice. As a result, VisitDeclRefExpr visits an
expression whose Decl is never visited and I can't bind it :frowning:

Since D<char>::f::c is present in ast-dump output as a distinct
ParmVarDecl node, I guess it should be visited. To do it in a non-hacky and
generic way, I guess I need to fix some Traverse* method in
RecursiveASTVisitor.h, but I'm a bit clueless as to what goes wrong. I've
checked TraverseFunctionHelper and its parameter handling, but can't find
anything wrong at a glance. Halp, plz? Any tips appreciated, I'm a rookie.

Which version of clang are you using? I thought this was fixed recently...

Cheers,
/Manuel

Which version of clang are you using? I thought this was fixed recently...

I've tested both clang 3.3 and the top of trunk, the behavior is the same.

Thanks,
Joshua