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? 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?