Search code examples
clangabstract-syntax-treellvm-clang

Appropriate AST Matcher for class parent declaration


Given a class hierarchy:

class A {};
class B {};
class C : public A {};
class D : public C {};

I'm trying to refactor class C to inherit from class B rather than class A. I can easily get the definition statement using a recordDecl:

recordDecl(hasName("C"), isDefinition()).bind("MyClass")

With an associated MatchCallback, I can dump() the class to verify it matched the right node. However I have not found a way to bind instead to the public A or, even better, just the A.

What would be an appropriate AST Matcher to capture either public A or A for class C?

EDIT:

The output of clang -ast-dump filtered for class C in this situation would show something similar to the following:

CXXRecordDecl [address] <line numbers> <loc> class C definition
|- public 'class A'
|- CXXRecordDecl [address 2] <columns> <loc> implicit referenced class C

It's as if there is no AST Node type to represent parent class declarations in the ast-dump.


Solution

  • I found this link providing some context for how to drill down. Notably, one tutorial suggested using RecordDecl in the match handler for the returned node vs. this reference using CXXRecordDecl. So if we output each base in the match handler:

    if (const CXXRecord *entityBase = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("entityBase")) {
        for (auto &p : entityBase->bases()) {
            p.dump();
        }
    }
    

    We find that the reference to class A is RecordType which is referred to by a CXXRecord. However using clang-query, I did not find a way to chain match recordType(...) with anything conducive to pulling just public A or A. Instead, the solution became something similar to this:

    std::string qual = "public ";
    std::string parentName = "A";
    std::string parentType = "class " + parentName;
    if (0 == parentType.compare(p.getType().getAsString())) {
        Replacement Rep(*(Result.SourceManager),
            p.getLocStart().getLocWithOffset(qual.length()),
            parentName.length(),
            "B");
        Replace->insert(Rep);
    }
    

    I'm not marking this as the answer yet just in case someone has a way to use recordType to snag the base class reference directly rather than iterating and checking base class type names, as shown above.