CXXRecordDecl::getNumBases() in matcher asserts under certain conditions

Hey all,

I've been working with AST Matchers with minimal issues for a while
now, but I recently started running into issues when querying matched
CXXRecordDecls for information about their base classes.

Here's a repro repo: https://github.com/nopppers/clang-bases-bug

Given the following matcher,

DeclarationMatcher CLASS_MATCHER
    = cxxRecordDecl(
        unless(isTemplateInstantiation()),
        hasAncestor(
            namespaceDecl(hasName("test"))
        )
      ).bind("class");

Calling CXXRecordDecl::getNumBases() or other base-class-related
functions like bases_begin() on the result of the matcher will
sometimes hit an assert. The debug message in the assert is
"queried property of class with no definition"

The first condition under which the assert will trigger is when the
matcher callback recieves the CXXRecordDecl for the implicit
self-reference that the class has. For example, if the matcher is run
on the following code:

namespace test
{
class Foo {};
}

The call to getNumBases() will succeed for the first match, test::Foo,
and assert for the match test::Foo::Foo.

To circumvent this, I've tried testing for
CXXRecordDecl::isImplicit(), which works around the assert properly.
There is, however, a second situation where the assert is triggered
that I don't understand.

Running the matcher on the following code

namespace test
{
    template <typename T>
    class DummyTemplate{};

    class Dummy
    {
    public:
        // DummyTemplate<int> uncommentMeAndTheAssertWontTrigger;
    };

    extern DummyTemplate<int> triggersAssertIfAboveIsCommentedOut;
}

Results in the same assert, but only if the variable declaration in
the class is commented out.
Adding a classTemplateSpecialization matcher reveals some more info:

Output with class var decl commented out:

Found CXXRecordDecl
NumBases for test::DummyTemplate: 0
End.
Found CXXRecordDecl
NumBases for test::DummyTemplate: Assertion failed: DD && "queried
property of class with no definition", file
S:\programming\libraries\clang\llvm\tools\clang\include\clang/AST/DeclCXX.h,
line 595

Output with var decl uncommented:

Found CXXRecordDecl
NumBases for test::DummyTemplate: 0
End.
Found ClassTemplateSpecializationDecl
NumBases for test::DummyTemplate: 0
End.
Found CXXRecordDecl
NumBases for test::Dummy: 0
End.

So what is one to do? I'm unsure whether hitting the assert is
intentional in either case. If it is, how can I avoid hitting it in
the second case?

Repro link again: https://github.com/nopppers/clang-bases-bug

Regards,
Jason Powers Murray

Hi Jason,

Do you care about the distinction between class declarations and class definitions? If not, the following matcher should suffice:

DeclarationMatcher CLASS_MATCHER
= cxxRecordDecl(

isDefinition(),

unless(isTemplateInstantiation()),
hasAncestor(
namespaceDecl(hasName(“test”))
)
).bind(“class”);

Similarly, in the code that handles the matcher you could check if the result is a definition. This latter approach is useful if you do care about this distinction.

-Adam

I suppose that’s why it’s called CXXRecordDecl, not CXXRecordDef. Thank you!

-Jason