What's the right way to match #includes (or #defines) using Clang's libtooling?
c++, clang, libtooling
Solution
I figured this out after digging around in Clang's Doxygen and code. I needed to be using the `PPCallbacks` class along with the `Preprocessor` class. An example is below. Note, this isn't guaranteed to be a functional code snippet, but it illustrates general usage. For more info, see Clang's PPCallbacks documentation and also the documentation for addPPCallbacks and getPPCallbacks in clang::Preprocessor.
class Find_Includes : public PPCallbacks
{
public:
bool has_include;
void InclusionDirective(
SourceLocation hash_loc,
const Token &include_token,
StringRef file_name,
bool is_angled,
CharSourceRange filename_range,
const FileEntry *file,
StringRef search_path,
StringRef relative_path,
const Module *imported)
{
// do something with the include
has_include = true;
}
};
class Include_Matching_Action : public ASTFrontendAction
{
bool BeginSourceFileAction(CompilerInstance &ci, StringRef)
{
std::unique_ptr<Find_Includes> find_includes_callback(new Find_Includes());
Preprocessor &pp = ci.getPreprocessor();
pp.addPPCallbacks(std::move(find_includes_callback));
return true;
}
void EndSourceFileAction()
{
CompilerInstance &ci = getCompilerInstance();
Preprocessor &pp = ci.getPreprocessor();
Find_Includes *find_includes_callback = static_cast<Find_Includes>(pp.getPPCallbacks());
// do whatever you want with the callback now
if (find_includes_callback->has_include)
std::cout << "Found at least one include" << std::endl;
}
};
Problem
I'm writing a libtooling refactoring tool. I have a class, let's say `Foo`, defined in a header called `foo.h`. I want to see if `foo.h` is included in a file. Currently, to check if `bar.cc` includes `foo.h`, I'm just matching using `recordDecl(hasName("Foo"))`. This works because `class Foo { ... };` will exist inside of `bar.cc`'s AST after preprocessing, if `bar.cc` includes `foo.h`. But this doesn't work if, for example, `bar.cc` includes `cat.h` which includes `foo.h`. I want `bar.cc` to EXPLICITLY include `foo.h`. Further, I'd like to be able to match `#define` macros. The way I've been writing my tool has made these two goals impossible because the AST I'm matching on has already been preprocessed. Is what I'm trying to do even possible? I dug around the `Preprocessor` class reference on Clang's Doxygen pages, but I haven't quite found what I'm looking for.