Getting the FieldDecl of a template class from a MemberExpr

Hi,

Using a RecursiveASTVisitor, I’ve been trying to rename field names against some conventions.

Let’s say I would like to rename Foo::bar into Foo::m_bar in the following code:

template
struct Foo
{
int bar;
};

int main()
{
Foo foo;
foo.bar = 2;

return 1;
}

In my custom visitor, I implement the following method:

bool VisitMemberExpr(MemberExpr* member)
{
ValueDecl* v = member->getMemberDecl(); // FieldDecl or CXXMethodDecl

}

But the FieldDecl I get when visiting the statement corresponding to “foo.bar=2;” is Foo::bar, but not Foo::bar.

So my question is: is it possible to get the FieldDecl of Foo::bar from the FieldDecl of Foo::bar ? at least when the struct is not specialized?
What happens when the struct is specialized?

Thanks,
Adrien

For fields, this is a pain. You'll actually have to look at the type that owns the field (a ClassTemplateSpecializationDecl), check whether it was instantiated vs. specialized (using getSpecializationKind()) and, if it was instantiated, retrieve the class template from which is was instantiated (getSpecializedTemplate()).

Utility functions to do this well---e.g., adding a FieldDecl *FieldDecl::getInstantiatedFrom() const member---would be welcome :slight_smile:

  - Doug

Douglas Gregor wrote:

Hi,

Using a RecursiveASTVisitor, I've been trying to rename field names
against some conventions.

Let's say I would like to rename Foo::bar into Foo::m_bar in the
following code:

template<typename T>
struct Foo
{
    int bar;
};

int main()
{
    Foo<double> foo;
    foo.bar = 2;

    return 1;
}

In my custom visitor, I implement the following method:

bool VisitMemberExpr(MemberExpr* member)
{
    ValueDecl* v = member->getMemberDecl(); // FieldDecl or CXXMethodDecl
    ...
}

But the FieldDecl I get when visiting the statement corresponding to
"foo.bar=2;" is Foo<double>::bar, but not Foo::bar.

So my question is: is it possible to get the FieldDecl of Foo<T>::bar
from the FieldDecl of Foo<double>::bar ? at least when the struct is not
specialized? What happens when the struct is specialized?

For fields, this is a pain. You'll actually have to look at the type that
owns the field (a ClassTemplateSpecializationDecl), check whether it was
instantiated vs. specialized (using getSpecializationKind()) and, if it
was instantiated, retrieve the class template from which is was
instantiated (getSpecializedTemplate()).

I was looking into the doxygen documentations, and I couldn't find how to
proceed from that.

How to get the correct field decl of the class template that correspond to
the field decl of the instantiated specialization? Can one just take the
std::distance of the field decl iterator for the specialization, and with
that, index into the field decl iterator of the class template?

That works, or you can use name lookup.

  - Doug

Thanks a lot!
Here is what I came up with (I didn’t make getInstantiatedFrom a member yet):

/// getInstantiatedFrom - if the parent of this (FieldDecl* d) is an
/// instanciation of a template class, return the corresponding FieldDecl* of this
/// template class.
FieldDecl* getInstantiatedFrom(FieldDecl const * const d)
{
ClassTemplateSpecializationDecl const * const parent =
dyn_cast(d->getParent());

// check the parent of this field is an instanciation of a class template
if (!parent || parent->getTemplateSpecializationKind() != TSK_ImplicitInstantiation)
return 0;

CXXRecordDecl const * const generic_parent =
parent->getSpecializedTemplate()->getTemplatedDecl();
assert(generic_parent);

for(CXXRecordDecl::field_iterator f=generic_parent->field_begin(), e=generic_parent->field_end(); f != e; ++f)
{
if ((*f)->getNameAsString() == d->getNameAsString())
return *f;
}

// should never get here
return 0;
}

Is it what you had in mind? I would be glad to make it a patch and add some tests it it seems ok.

Adrien

Thanks a lot!
Here is what I came up with (I didn’t make getInstantiatedFrom a member yet):

/// getInstantiatedFrom - if the parent of this (FieldDecl* d) is an
/// instanciation of a template class, return the corresponding FieldDecl* of this
/// template class.
FieldDecl* getInstantiatedFrom(FieldDecl const * const d)
{
ClassTemplateSpecializationDecl const * const parent =
dyn_cast(d->getParent());

CXXRecordDecls themselves can also be instantiations of member classes. There’s a getInstantiatedFromMember() function (or something like it) to determine when this is the case.

// check the parent of this field is an instanciation of a class template
if (!parent || parent->getTemplateSpecializationKind() != TSK_ImplicitInstantiation)
return 0;

Explicit instantiations should also be considered here.

CXXRecordDecl const * const generic_parent =
parent->getSpecializedTemplate()->getTemplatedDecl();
assert(generic_parent);

for(CXXRecordDecl::field_iterator f=generic_parent->field_begin(), e=generic_parent->field_end(); f != e; ++f)
{
if ((*f)->getNameAsString() == d->getNameAsString())
return *f;
}

DeclContex::lookup() can perform this search faster.

Thanks! I’m going to work on that!