Reputation: 31
I want to get AST of main function in source file (assuming there is one) to build control flow graph out of it. I found code that generates and traverse AST here: https://shaharmike.com/cpp/libclang/. But the problem is it goes into all included files. I also find this topic: Clang AST visitor, avoid traversing include files. But it seems that in clang10 some changes were made and suggested solution don't work now. Or maybe there is some other way to get AST for build control flow graph? Only requirement - it must work C++ source code.
Upvotes: 0
Views: 1102
Reputation: 31
So, thanks to Botje and this post(https://www.phototalks.idv.tw/academic/?p=1932) I find the solution. Not the best code in any means, but it works. I hope it'll help others.
#include <iostream>
#include <clang-c/Index.h>
#include <string.h>
using namespace std;
ostream& operator<<(ostream& stream, const CXString& str)
{
stream << clang_getCString(str);
clang_disposeString(str);
return stream;
}
std::string getCursorKindName( CXCursorKind cursorKind )
{
CXString kindName = clang_getCursorKindSpelling( cursorKind );
std::string result = clang_getCString( kindName );
clang_disposeString( kindName );
return result;
}
std::string getCursorSpelling( CXCursor cursor )
{
CXString cursorSpelling = clang_getCursorSpelling( cursor );
std::string result = clang_getCString( cursorSpelling );
clang_disposeString( cursorSpelling );
return result;
}
CXChildVisitResult visitor1( CXCursor cursor, CXCursor /* parent */, CXClientData clientData )
{
CXSourceLocation location = clang_getCursorLocation( cursor );
unsigned int locationstring =0;
clang_getSpellingLocation ( location, NULL, &locationstring, NULL,NULL);
if( clang_Location_isFromMainFile( location ) == 0 )
return CXChildVisit_Continue;
CXCursorKind kind = clang_getCursorKind(cursor);
std::string str2 ("main");
CXCursorKind cursorKind = clang_getCursorKind( cursor );
unsigned int curLevel = *( reinterpret_cast<unsigned int*>( clientData ) );
unsigned int nextLevel = curLevel + 1;
std::cout << std::string( curLevel, '-' ) << " " << getCursorKindName(
cursorKind ) << " (" << getCursorSpelling( cursor ) << ") ";
std::cout << locationstring ;
std::cout << endl;
clang_visitChildren( cursor,
visitor1,
&nextLevel );
return CXChildVisit_Continue;
}
CXChildVisitResult visitor( CXCursor cursor, CXCursor /* parent */, CXClientData clientData )
{
CXSourceLocation location = clang_getCursorLocation( cursor );
if( clang_Location_isFromMainFile( location ) == 0 )
return CXChildVisit_Continue;
CXCursorKind kind = clang_getCursorKind(cursor);
std::string str2 ("main");
CXCursorKind cursorKind = clang_getCursorKind( cursor );
unsigned int curLevel = *( reinterpret_cast<unsigned int*>( clientData ) );
unsigned int nextLevel = curLevel + 1;
if (!((str2.compare(getCursorSpelling(cursor)))))
{
std::cout << std::string( curLevel, '-' ) << " " << getCursorKindName(
cursorKind ) << " (" << getCursorSpelling( cursor ) << ")\n";
clang_visitChildren( cursor,
visitor1,
&nextLevel );
return CXChildVisit_Continue;
}
else
{
return CXChildVisit_Continue;
}
}
int main()
{
CXIndex index = clang_createIndex(0, 0);
CXTranslationUnit unit = clang_parseTranslationUnit(
index,
<source_file_name>, nullptr, 0,
nullptr, 0,
CXTranslationUnit_None);
CXCursor cursor = clang_getTranslationUnitCursor(unit);
unsigned int treeLevel = 0;
clang_visitChildren( cursor, visitor, &treeLevel );
clang_disposeTranslationUnit(unit);
clang_disposeIndex(index);
}
Upvotes: 0
Reputation: 30830
Reading the documentation teaches me that clang_visitChildren
only goes "into" whatever it is pointing to if you return CXChildVisit_Recurse
. So your visit function should inspect the cursor kind and return CXChildVisit_Continue
until it reaches a function definition where the name equals main
. What it does with main
is up to you, but I suggest returning CXChildVisit_Break
.
Upvotes: 2