| .. role:: raw-html(raw) |
| :format: html |
| |
| Libclang tutorial |
| ================= |
| The C Interface to Clang provides a relatively small API that exposes facilities for parsing source code into an abstract syntax tree (AST), loading already-parsed ASTs, traversing the AST, associating physical source locations with elements within the AST, and other facilities that support Clang-based development tools. |
| This C interface to Clang will never provide all of the information representation stored in Clang's C++ AST, nor should it: the intent is to maintain an API that is relatively stable from one release to the next, providing only the basic functionality needed to support development tools. |
| The entire C interface of libclang is available in the file `Index.h`_ |
| |
| Essential types overview |
| ------------------------- |
| |
| All types of libclang are prefixed with ``CX`` |
| |
| CXIndex |
| ~~~~~~~ |
| An Index that consists of a set of translation units that would typically be linked together into an executable or library. |
| |
| CXTranslationUnit |
| ~~~~~~~~~~~~~~~~~ |
| A single translation unit, which resides in an index. |
| |
| CXCursor |
| ~~~~~~~~ |
| A cursor representing a pointer to some element in the abstract syntax tree of a translation unit. |
| |
| |
| Code example |
| """""""""""" |
| |
| .. code-block:: cpp |
| |
| // file.cpp |
| struct foo{ |
| int bar; |
| int* bar_pointer; |
| }; |
| |
| .. code-block:: cpp |
| |
| #include <clang-c/Index.h> |
| #include <iostream> |
| |
| int main(){ |
| CXIndex index = clang_createIndex(0, 0); //Create index |
| CXTranslationUnit unit = clang_parseTranslationUnit( |
| index, |
| "file.cpp", nullptr, 0, |
| nullptr, 0, |
| CXTranslationUnit_None); //Parse "file.cpp" |
| |
| |
| if (unit == nullptr){ |
| std::cerr << "Unable to parse translation unit. Quitting.\n"; |
| return 0; |
| } |
| CXCursor cursor = clang_getTranslationUnitCursor(unit); //Obtain a cursor at the root of the translation unit |
| } |
| |
| Visiting elements of an AST |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| The elements of an AST can be recursively visited with pre-order traversal with ``clang_visitChildren``. |
| |
| .. code-block:: cpp |
| |
| clang_visitChildren( |
| cursor, //Root cursor |
| [](CXCursor current_cursor, CXCursor parent, CXClientData client_data){ |
| |
| CXString current_display_name = clang_getCursorDisplayName(current_cursor); |
| //Allocate a CXString representing the name of the current cursor |
| |
| std::cout << "Visiting element " << clang_getCString(current_display_name) << "\n"; |
| //Print the char* value of current_display_name |
| |
| clang_disposeString(current_display_name); |
| //Since clang_getCursorDisplayName allocates a new CXString, it must be freed. This applies |
| //to all functions returning a CXString |
| |
| return CXChildVisit_Recurse; |
| |
| |
| }, //CXCursorVisitor: a function pointer |
| nullptr //client_data |
| ); |
| |
| The return value of ``CXCursorVisitor``, the callable argument of ``clang_visitChildren``, can return one of the three: |
| |
| #. ``CXChildVisit_Break``: Terminates the cursor traversal |
| |
| #. ``CXChildVisit_Continue``: Continues the cursor traversal with the next sibling of the cursor just visited, without visiting its children. |
| |
| #. ``CXChildVisit_Recurse``: Recursively traverse the children of this cursor, using the same visitor and client data |
| |
| The expected output of that program is |
| |
| .. code-block:: |
| |
| Visiting element foo |
| Visiting element bar |
| Visiting element bar_pointer |
| |
| |
| Extracting information from a Cursor |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| .. The following functions take a ``CXCursor`` as an argument and return associated information. |
| |
| |
| |
| Extracting the Cursor kind |
| """""""""""""""""""""""""" |
| |
| ``CXCursorKind clang_getCursorKind(CXCursor)`` Describes the kind of entity that a cursor refers to. Example values: |
| |
| - ``CXCursor_StructDecl``: A C or C++ struct. |
| - ``CXCursor_FieldDecl``: A field in a struct, union, or C++ class. |
| - ``CXCursor_CallExpr``: An expression that calls a function. |
| |
| |
| Extracting the Cursor type |
| """""""""""""""""""""""""" |
| ``CXType clang_getCursorType(CXCursor)``: Retrieve the type of a CXCursor (if any). |
| |
| A ``CXType`` represents a complete C++ type, including qualifiers and pointers. It has a member field ``CXTypeKind kind`` and additional opaque data. |
| |
| Example values for ``CXTypeKind kind`` |
| |
| - ``CXType_Invalid``: Represents an invalid type (e.g., where no type is available) |
| - ``CXType_Pointer``: A pointer to another type |
| - ``CXType_Int``: Regular ``int`` |
| - ``CXType_Elaborated``: Represents a type that was referred to using an elaborated type keyword e.g. struct S, or via a qualified name, e.g., N::M::type, or both. |
| |
| Any ``CXTypeKind`` can be converted to a ``CXString`` using ``clang_getTypeKindSpelling(CXTypeKind)``. |
| |
| A ``CXType`` holds additional necessary opaque type info, such as: |
| |
| - Which struct was referred to? |
| - What type is the pointer pointing to? |
| - Qualifiers (e.g. ``const``, ``volatile``)? |
| |
| Qualifiers of a ``CXType`` can be queried with: |
| |
| - ``clang_isConstQualifiedType(CXType)`` to check for ``const`` |
| - ``clang_isRestrictQualifiedType(CXType)`` to check for ``restrict`` |
| - ``clang_isVolatileQualifiedType(CXType)`` to check for ``volatile`` |
| |
| Code example |
| """""""""""" |
| .. code-block:: cpp |
| |
| //structs.cpp |
| struct A{ |
| int value; |
| }; |
| struct B{ |
| int value; |
| A struct_value; |
| }; |
| |
| .. code-block:: cpp |
| |
| #include <clang-c/Index.h> |
| #include <iostream> |
| |
| int main(){ |
| CXIndex index = clang_createIndex(0, 0); //Create index |
| CXTranslationUnit unit = clang_parseTranslationUnit( |
| index, |
| "structs.cpp", nullptr, 0, |
| nullptr, 0, |
| CXTranslationUnit_None); //Parse "structs.cpp" |
| |
| if (unit == nullptr){ |
| std::cerr << "Unable to parse translation unit. Quitting.\n"; |
| return 0; |
| } |
| CXCursor cursor = clang_getTranslationUnitCursor(unit); //Obtain a cursor at the root of the translation unit |
| |
| clang_visitChildren( |
| cursor, |
| [](CXCursor current_cursor, CXCursor parent, CXClientData client_data){ |
| CXType cursor_type = clang_getCursorType(current_cursor); |
| |
| CXString type_kind_spelling = clang_getTypeKindSpelling(cursor_type.kind); |
| std::cout << "Type Kind: " << clang_getCString(type_kind_spelling); |
| clang_disposeString(type_kind_spelling); |
| |
| if(cursor_type.kind == CXType_Pointer || // If cursor_type is a pointer |
| cursor_type.kind == CXType_LValueReference || // or an LValue Reference (&) |
| cursor_type.kind == CXType_RValueReference){ // or an RValue Reference (&&), |
| CXType pointed_to_type = clang_getPointeeType(cursor_type);// retrieve the pointed-to type |
| |
| CXString pointed_to_type_spelling = clang_getTypeSpelling(pointed_to_type); // Spell out the entire |
| std::cout << "pointing to type: " << clang_getCString(pointed_to_type_spelling);// pointed-to type |
| clang_disposeString(pointed_to_type_spelling); |
| } |
| else if(cursor_type.kind == CXType_Record){ |
| CXString type_spelling = clang_getTypeSpelling(cursor_type); |
| std::cout << ", namely " << clang_getCString(type_spelling); |
| clang_disposeString(type_spelling); |
| } |
| std::cout << "\n"; |
| return CXChildVisit_Recurse; |
| }, |
| nullptr |
| ); |
| |
| The expected output of program is: |
| |
| .. code-block:: |
| |
| Type Kind: Record, namely A |
| Type Kind: Int |
| Type Kind: Record, namely B |
| Type Kind: Int |
| Type Kind: Record, namely A |
| Type Kind: Record, namely A |
| |
| |
| Reiterating the difference between ``CXType`` and ``CXTypeKind``: For an example |
| |
| .. code-block:: cpp |
| |
| const char* __restrict__ variable; |
| |
| - Type Kind will be: ``CXType_Pointer`` spelled ``"Pointer"`` |
| - Type will be a complex ``CXType`` structure, spelled ``"const char* __restrict__`` |
| |
| Retrieving source locations |
| """"""""""""""""""""""""""" |
| |
| ``CXSourceRange clang_getCursorExtent(CXCursor)`` returns a ``CXSourceRange``, representing a half-open range in the source code. |
| |
| Use ``clang_getRangeStart(CXSourceRange)`` and ``clang_getRangeEnd(CXSourceRange)`` to retrieve the starting and end ``CXSourceLocation`` from a source range, respectively. |
| |
| Given a ``CXSourceLocation``, use ``clang_getExpansionLocation`` to retrieve file, line and column of a source location. |
| |
| Code example |
| """""""""""" |
| .. code-block:: cpp |
| |
| // Again, file.cpp |
| struct foo{ |
| int bar; |
| int* bar_pointer; |
| }; |
| .. code-block:: cpp |
| |
| clang_visitChildren( |
| cursor, |
| [](CXCursor current_cursor, CXCursor parent, CXClientData client_data){ |
| |
| CXType cursor_type = clang_getCursorType(current_cursor); |
| CXString cursor_spelling = clang_getCursorSpelling(current_cursor); |
| CXSourceRange cursor_range = clang_getCursorExtent(current_cursor); |
| std::cout << "Cursor " << clang_getCString(cursor_spelling); |
| |
| CXFile file; |
| unsigned start_line, start_column, start_offset; |
| unsigned end_line, end_column, end_offset; |
| |
| clang_getExpansionLocation(clang_getRangeStart(cursor_range), &file, &start_line, &start_column, &start_offset); |
| clang_getExpansionLocation(clang_getRangeEnd (cursor_range), &file, &end_line , &end_column , &end_offset); |
| std::cout << " spanning lines " << start_line << " to " << end_line; |
| clang_disposeString(cursor_spelling); |
| |
| std::cout << "\n"; |
| return CXChildVisit_Recurse; |
| }, |
| nullptr |
| ); |
| |
| The expected output of this program is: |
| |
| .. code-block:: |
| |
| Cursor foo spanning lines 2 to 5 |
| Cursor bar spanning lines 3 to 3 |
| Cursor bar_pointer spanning lines 4 to 4 |
| |
| Complete example code |
| ~~~~~~~~~~~~~~~~~~~~~ |
| |
| .. code-block:: cpp |
| |
| #include <clang-c/Index.h> |
| #include <iostream> |
| |
| int main(){ |
| CXIndex index = clang_createIndex(0, 0); //Create index |
| CXTranslationUnit unit = clang_parseTranslationUnit( |
| index, |
| "file.cpp", nullptr, 0, |
| nullptr, 0, |
| CXTranslationUnit_None); //Parse "file.cpp" |
| |
| if (unit == nullptr){ |
| std::cerr << "Unable to parse translation unit. Quitting.\n"; |
| return 0; |
| } |
| CXCursor cursor = clang_getTranslationUnitCursor(unit); //Obtain a cursor at the root of the translation unit |
| |
| |
| clang_visitChildren( |
| cursor, |
| [](CXCursor current_cursor, CXCursor parent, CXClientData client_data){ |
| CXType cursor_type = clang_getCursorType(current_cursor); |
| |
| CXString type_kind_spelling = clang_getTypeKindSpelling(cursor_type.kind); |
| std::cout << "TypeKind: " << clang_getCString(type_kind_spelling); |
| clang_disposeString(type_kind_spelling); |
| |
| if(cursor_type.kind == CXType_Pointer || // If cursor_type is a pointer |
| cursor_type.kind == CXType_LValueReference || // or an LValue Reference (&) |
| cursor_type.kind == CXType_RValueReference){ // or an RValue Reference (&&), |
| CXType pointed_to_type = clang_getPointeeType(cursor_type);// retrieve the pointed-to type |
| |
| CXString pointed_to_type_spelling = clang_getTypeSpelling(pointed_to_type); // Spell out the entire |
| std::cout << "pointing to type: " << clang_getCString(pointed_to_type_spelling);// pointed-to type |
| clang_disposeString(pointed_to_type_spelling); |
| } |
| else if(cursor_type.kind == CXType_Record){ |
| CXString type_spelling = clang_getTypeSpelling(cursor_type); |
| std::cout << ", namely " << clang_getCString(type_spelling); |
| clang_disposeString(type_spelling); |
| } |
| std::cout << "\n"; |
| return CXChildVisit_Recurse; |
| }, |
| nullptr |
| ); |
| |
| |
| clang_visitChildren( |
| cursor, |
| [](CXCursor current_cursor, CXCursor parent, CXClientData client_data){ |
| |
| CXType cursor_type = clang_getCursorType(current_cursor); |
| CXString cursor_spelling = clang_getCursorSpelling(current_cursor); |
| CXSourceRange cursor_range = clang_getCursorExtent(current_cursor); |
| std::cout << "Cursor " << clang_getCString(cursor_spelling); |
| |
| CXFile file; |
| unsigned start_line, start_column, start_offset; |
| unsigned end_line, end_column, end_offset; |
| |
| clang_getExpansionLocation(clang_getRangeStart(cursor_range), &file, &start_line, &start_column, &start_offset); |
| clang_getExpansionLocation(clang_getRangeEnd (cursor_range), &file, &end_line , &end_column , &end_offset); |
| std::cout << " spanning lines " << start_line << " to " << end_line; |
| clang_disposeString(cursor_spelling); |
| |
| std::cout << "\n"; |
| return CXChildVisit_Recurse; |
| }, |
| nullptr |
| ); |
| } |
| |
| |
| .. _Index.h: https://github.com/llvm/llvm-project/blob/main/clang/include/clang-c/Index.h |