PhantomR
PhantomR

Reputation: 595

How to get function definition/signature as a string in Clang?

How can I get a function's signature (or at least the entire definition?) as a string using Clang/Libclang, assuming I have its CXCursor or so?

I think that the definition may be somehow obtainable by using the cursor's extents, but I don't really know how (what function to use).

Upvotes: 5

Views: 5416

Answers (4)

veio
veio

Reputation: 537

I use the following, shorter way with clang 10 (though its using a matcher, not a cursor): The 2 helper functions are general helpers to get the code snippet as a string.

// helper function 1: find position of end of token    
SourceLocation
end_of_the_end(SourceLocation const & start_of_end, SourceManager & sm)
{
  using namespace clang;
  LangOptions lopt;
  return Lexer::getLocForEndOfToken(start_of_end, 0, sm, lopt);
}

  // helper function 2: 
  std::string getSymbolString(clang::SourceManager & sm,
                              const clang::SourceRange & range)
  {
    return std::string(sm.getCharacterData(range.getBegin()),
                       sm.getCharacterData(end_of_the_end(range.getEnd(), sm)));
  }

The actual code snippet to get the function declaration string is:

// ... in run() of a matcher:
  virtual void run(corct::result_t const & result) override
  {
    using namespace clang;
    FunctionDecl * f_decl = const_cast<FunctionDecl *>(
        result.Nodes.getNodeAs<FunctionDecl>(fd_bd_name_));

    if(f_decl) {
      SourceManager & sm(result.Context->getSourceManager());
      FunctionDecl * f_decl = const_cast<FunctionDecl *>(
          result.Nodes.getNodeAs<FunctionDecl>(fd_bd_name_));
      auto full_decl_string =
          getSymbolString(sm, f_decl->DeclaratorDecl::getSourceRange());
    }
 }

This will output inline bool test2(std::string const & str, std::vector<std::string> & sss) for the following function:

  inline bool test2(std::string const & str, std::vector<std::string> & sss)
  {
    return true;
  }

Upvotes: 1

CRB
CRB

Reputation: 121

I'm using the following for a project I am working on and it works great for the definition. TL&DR clang_getCursorPrettyPrinted with the policy TerseOutput set to true.



    std::string getStdString(const CXString &s)
    {
        std::string rv = clang_getCString(s);
        clang_disposeString(s);
        return rv;
    }
    
    bool isFunctionImplementation(CXCursor &cursor,std::string &decl,std::string &filename,unsigned &lineno)
    {
        std::string cs = getStdString(clang_getCursorPrettyPrinted(cursor,nullptr));
        if (cs.find('{') == std::string::npos) // Just a declaration, not the "meat" of the function, so we dont care
            return false;
        clang::LangOptions lo;
        struct clang::PrintingPolicy pol(lo);
        pol.adjustForCPlusPlus();
        pol.TerseOutput = true;
        pol.FullyQualifiedName = true;
        decl = getStdString(clang_getCursorPrettyPrinted(cursor,&pol));
        CXSourceLocation location = clang_getCursorLocation( cursor );
        CXFile f;
        lineno = 0;
        filename = "(None)";
        clang_getSpellingLocation(location,&f,&lineno,nullptr,nullptr);
        if (lineno)
        {
            filename = getStdString(clang_File_tryGetRealPathName(f));
        }
        return isAllowedDirectory(filename);
    }

This one checks if the function call is "meat" or just a definition. Obviously you can adjust as needed, including writing your own isAllowedDirectory function. Just pass the cursor, two strings and an unsigned into this as you walk your AST when you hit a declaration type.

Upvotes: 0

backspace
backspace

Reputation: 1

You could try using clang_Cursor_getMangling() and demangle the result in order to get the complete definition.

Upvotes: 0

EIP Passenger
EIP Passenger

Reputation: 73

You can use this simple code to get prototype of a function ( name, return type, count of arguments and arguments[name,data type]).

 string Convert(const CXString& s)
{
    string result = clang_getCString(s);
    clang_disposeString(s);
    return result;
}
void print_function_prototype(CXCursor cursor)
{
    // TODO : Print data! 
    auto type = clang_getCursorType(cursor);


    auto function_name = Convert(clang_getCursorSpelling(cursor));
    auto return_type   = Convert(clang_getTypeSpelling(clang_getResultType(type)));

    int num_args = clang_Cursor_getNumArguments(cursor);
    for (int i = 0; i < num_args; ++i)
    {
        auto arg_cursor = clang_Cursor_getArgument(cursor, i);
        auto arg_name = Convert(clang_getCursorSpelling(arg_cursor));
        if (arg_name.empty())
        {
            arg_name = "no name!";
        }

        auto arg_data_type = Convert(clang_getTypeSpelling(clang_getArgType(type, i)));
    }
}
CXChildVisitResult functionVisitor(CXCursor cursor, CXCursor /* parent */, CXClientData /* clientData */)
{
    if (clang_Location_isFromMainFile(clang_getCursorLocation(cursor)) == 0)
        return CXChildVisit_Continue;

    CXCursorKind kind = clang_getCursorKind(cursor);
    if ((kind == CXCursorKind::CXCursor_FunctionDecl || kind == CXCursorKind::CXCursor_CXXMethod || kind == CXCursorKind::CXCursor_FunctionTemplate || \
         kind == CXCursorKind::CXCursor_Constructor))
    {
        print_function_prototype(cursor);
    }

    return CXChildVisit_Continue;
}

Upvotes: 6

Related Questions