Reputation: 337
I'm a novice for Clang who is trying to analyze AST via libtooling. I want to find a particular function, and move its AST from original source file to a new file.
I've known how to find the function by MatchFinder. Now, I was wondering how to write its AST to a new file(.c or .cpp)
Thanks in advance!
Upvotes: 3
Views: 1774
Reputation: 1121
Summary: to get the source text, work with the SourceManager
; to remove the function from the original file, generate a Replacement
and apply it with a RefactoringTool
.
First, here's a way to get at the source code for a function definition, assuming an AST matcher that looks something like this:
auto matcher(std::string const & fname) {
return functionDecl(hasName(fname)).bind("f_decl");
}
The run method of the Callback would begin by getting access to the matched AST node, getting the source range covered by the function declaration, and getting a reference to the SouceManager, which relates the SourceLocation objects to the actual source:
virtual void run(MatchResult_t const & result) override {
using namespace clang;
FunctionDecl * f_decl = const_cast<FunctionDecl *>(
result.Nodes.getNodeAs<FunctionDecl>("f_decl"));
if(f_decl) {
SourceManager &sm(result.Context->getSourceManager());
SourceRange decl_range(f_decl->getSourceRange());
SourceLocation decl_begin(decl_range.getBegin());
SourceLocation decl_start_end(decl_range.getEnd());
SourceLocation decl_end_end( end_of_the_end( decl_start_end,sm));
What's with decl_start_end
and decl_end_end
? There is one catch to using a SourceRange: the ending location is not where the code ends; it is the beginning of the last token in the range. So if we go to the SourceManager with decl_range.getEnd()
for a function definition, we won't get the closing curly bracket. end_of_the_end()
uses a lexer to get the location of the last bit of the code:
SourceLocation
end_of_the_end(SourceLocation const & start_of_end, SourceManager & sm){
LangOptions lopt;
return Lexer::getLocForEndOfToken(start_of_end, 0, sm, lopt);
}
Back in run()
, with accurate beginning and ending locations, you can get pointers into the SourceManager's character buffer:
const char * buff_begin( sm.getCharacterData(decl_begin));
const char * buff_end( sm.getCharacterData(decl_end_end));
std::string const func_string(buff_begin,buff_end);
func_string has the function's source code; you can write to new file etc.
To eliminate the function's source in the original file, we can generate a Replacement, and let the RefactoringTool apply that for us. To create a Replacement, we need to add two more lines of code to run()
:
uint32_t const decl_length =
sm.getFileOffset(decl_end_end) - sm.getFileOffset(decl_begin);
Replacement repl(sm,decl_begin,decl_length,"");
The Replacement ctor takes the SourceManager, where to begin replacing, how much to overwrite, and what to overwrite with. This Replacement overwrites the entire original function definition with nothing.
How do we get that Replacement to the RefactoringTool? We could construct the callback class with a reference to the RefactoringTool's Replacements member. In run
, one would then conclude:
repls_.insert(repl);
I've added a working example application in apps/FunctionMover.cc in CoARCT, a collection of Clang refactoring examples.
Upvotes: 6