Implicit Generation

I have a tool built using clang that parses a C++ file, explicitly generates methods (e.g. copy constructors), and then prints the resulting AST to a file. This works for simple code, but when template instantiations start coming into play, I can not figure out a (reliable) way to get a handle on a mutable ClassTemplateSpecializationDecl when a templated-class is instantiated.

I have set up an ASTConsumer to notify me of each TagDecl that is in the file and I generate its implicit methods using the Sema object. Specifically, I generate the definitions in HandleTagDeclDefinition and generate the from HandleTopLevelDecl. To be notified when a template instantiation forces the generation of another template specialization, I need to use an ASTMutationListener; however, the declarations passed to the callbacks on ASTMutationListener are const so I can not add the implicit members to them without doing a const_cast. The logic is roughly:

class Impl : public clang::ASTConsumer, clang::ASTMutationListener {
public:
    // Implementation of `clang::ASTConsumer`

    virtual void HandleTagDeclDefinition(TagDecl *decl) override { elab(i); }
    virtual void HandleTagDeclRequiredDefinition(const TagDecl *decl) override {
       // [TagDecl] is [const], so I can't do anything here
    }

    virtual bool HandleTopLevelDecl(DeclGroupRef decl) override {
        for (auto i : decl) {
            elab(i, true);
        }
    }
    virtual void HandleInlineFunctionDefinition(FunctionDecl *decl) override { elab(decl, true); }
    virtual void
    HandleCXXImplicitFunctionInstantiation(FunctionDecl *decl) override;
    virtual ASTMutationListener *GetASTMutationListener() override {
        return this;
    }

public:
    // Implementation of clang::ASTMutationListener
    virtual void
    AddedCXXTemplateSpecialization(const ClassTemplateDecl *TD,
                                   const ClassTemplateSpecializationDecl *D) {
        // The implementation calls this method from a non-`const` method.
        // it is not clear (to me) why this method should take a
        // `const ClassTemplateSpecializationDecl` rather than a non-`const`
        elab(const_cast<ClassTemplateSpecializationDecl *>(D), true);
    }

private:
    void elab(Decl *, bool = false); // function that I need to call on every declaration/definition
};

This works in my test cases, and it seems (see comment) that the const_cast is not UB according to C++ since the method that is calling this function is calling it on a mutable object; however, the need for a const_cast is always concerning. I’m wondering if there is something in the API that I am missing? The documentation seems to suggest that ASTConsumer is mostly meant for reading, though it does give back mutable pointers to the nodes. Is there some way that I can be notified of this with a mutable object so that I can perform the elaboration? Why does this method take a const object?

As a note, I was previously using enableIncrementalProcessing and the solution here (also my question), but that solution stopped working for me with clang16 with an error when I try to compile a file like:

class C { ~C(); };
C::~C() { }

Because it seems that clang16 thinks that C::~C() is a call to a non-static member function rather than looking ahead and seeing that it is really the declaration of that function.