Trigger template instantiation with libtooling


I’d like to trigger explicit template instantiations from within clang plugin/tool, how could I achieve it?
Algorithm I’m looking to implement looks like this:
Input: AST + pairs of (X, X_model) template classes. X_model will be instantiable with same template arguments as X
Condition: There is instantiation of class X in the AST (where T is concrete type)
Result: Additional explicit instantiation of X_model is added to AST

I’ve looked around clang codebase, but I see no obvious way to do that. Two approaches I was able to spot:

  1. Call functions from Sema. Sema::ActOnExplicitInstantiation has code that looks relevant to my problem. It seems to be deep into clang internals though (and I never used Sema inside plugin/tool before)
  2. clang static analyzer already has a concept of .model files that produce extra AST for functions defined outside of TranslationUnit:

I’m working on static analysis tool called infer and we have clang plugin that exports AST (in json-like format) which then is read by our frontend. We rely on clang to do template instantiations for us and it’s been working great so far.
However, in order for our tool to better understand standard library, we write simplified versions (we call them models) of some functions.
In C it means writing code for memcpy etc and then ‘linking’ this code with the rest of the program [1]
In C++ it means that we want to write simplified versions of std::shared_ptr, std::unique_ptr, std::vector etc. The challenge here is that we need clang to instantiate simplified code with concrete types [2]
Current solution involves writing our own version of std::vector header which compiles with the rest of stdlibc++ and libc++. Then, we replace standard header with our header and run compilation. This approach is hard to write and maintain in the long run - we have dense templated code which has nothing to do with our models, but required for compilation to succeed. Most importantly, maintaining compatibility with future versions of stdlibc++ and libc++ is non trivial.
The solution I’m exploring now is the following:

  1. Every time we see std::vector instantiation, instantiate infer_std::vector with same arguments.
  2. Export AST with instantiations of infer_std::vector
  3. In infer’s frontend do some matching of fields/methods between std::vector and infer_std::vector and then use infer_std::vector instead of std::vector. In case of failure (such as no right method in infer_std::vector class), carry on instead of failing.

[1] This is how memcpy model look like - it’s fairly easy to understand actual logic:

[2] Currently our vector model looks like this – right now we need to get everything right for compilation to succeed.