Skip to content

Instantly share code, notes, and snippets.

@pftbest
Created March 6, 2024 19:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pftbest/1cfcd41faa376f971f2ee4f5f66f5c78 to your computer and use it in GitHub Desktop.
Save pftbest/1cfcd41faa376f971f2ee4f5f66f5c78 to your computer and use it in GitHub Desktop.

When using libclang, the Clang C interface for parsing C/C++ code, clang_reparseTranslationUnit can be used to update an existing CXTranslationUnit rather than creating an entirely new one. This allows you to reparse the source files in case there are changes while preserving the already parsed information for unchanged files, such as headers. By doing this, parsing will be faster as it avoids reparsing the unchanged header files.

Here's a step-by-step guide on how to use clang_reparseTranslationUnit:

  1. Create a CXIndex: The first step is to create an index object that will be used throughout the parsing process.
CXIndex index = clang_createIndex(0, 0);
  1. Parse the Source File: Initially, parse the source file(s) to create a CXTranslationUnit. You might want to add parsing options as needed for your specific case.
const char *source_filename = "your_source_file.c";
CXTranslationUnit translation_unit;
clang_parseTranslationUnit2(
    index,
    source_filename,
    NULL, 0,             /* command_line_args, num_command_line_args */
    NULL, 0,             /* unsaved_files, num_unsaved_files */
    CXTranslationUnit_None, /* options */
    &translation_unit
);
  1. Modify the Source: At this point, you would typically make changes to the source file(s) that were parsed. This usually happens outside of libclang.

  2. Prepare Unsaved Files: If you have modified the contents of files but haven't saved them yet, you'll need to prepare unsaved file structures (CXUnsavedFile) to pass to clang_reparseTranslationUnit.

struct CXUnsavedFile unsaved_files[] = {
    { "your_source_file.c", "new content here", strlen("new content here") }
    // Add any additional unsaved files as needed.
};
unsigned num_unsaved_files = 1; // Set to the number of unsaved files you have
  1. Reparse the CXTranslationUnit: Now that your modifications are ready, use clang_reparseTranslationUnit to reparse the source files.
clang_reparseTranslationUnit(
    translation_unit,
    num_unsaved_files,
    unsaved_files,
    CXReparse_None   /* Reparsing options, currently always 0 */
);
  1. Work With the Updated translation_unit: After reparsing, the translation_unit will contain the updated AST.

  2. Clean Up: When you're done with the translation_unit, be sure to dispose of it to prevent memory leaks. Also, dispose of the index you created at the beginning.

clang_disposeTranslationUnit(translation_unit);
clang_disposeIndex(index);

Key Point to Avoid Reparsing Header Files: To avoid reparsing header files, ensure you do not modify them between calls to clang_reparseTranslationUnit. libclang is designed to reparse only the modified source files (those listed as unsaved files with changes), and it will try to avoid parsing unchanged headers.

Please remember that this example assumes you are interfacing directly with the libclang API in C. Error handling and detailed configuration (e.g., specifying include directories, preprocessor definitions, etc.) through clang_parseTranslationUnit2 function options are omitted for brevity. Make sure to adjust these steps according to your specific use case and to handle errors appropriately in your application. Please print the whole example as main.c file Below is an example of a main.c file that demonstrates the usage of clang_reparseTranslationUnit in a simple C application. This example assumes that you already have the libclang development files installed on your system.

#include <clang-c/Index.h>
#include <stdio.h>
#include <string.h>

int main() {
    // 1. Create a CXIndex
    CXIndex index = clang_createIndex(0, 0);

    // 2. Parse the Source File
    const char *source_filename = "example.c";
    CXTranslationUnit translation_unit;
    enum CXErrorCode parse_error = clang_parseTranslationUnit2(
        index,
        source_filename,
        NULL, 0,               // command_line_args, num_command_line_args
        NULL, 0,               // unsaved_files, num_unsaved_files
        CXTranslationUnit_None, // options
        &translation_unit
    );
    if (parse_error != CXError_Success) {
        fprintf(stderr, "Error: Unable to parse translation unit. Quitting.\n");
        clang_disposeIndex(index);
        return -1;
    }

    // 3. Modify the Source (done externally, simulate using unsaved_files)

    // Dummy contents to simulate file change
    const char *new_file_content =
        "int main() { return 0; }";

    // 4. Prepare Unsaved Files
    struct CXUnsavedFile unsaved_files[] = {
        { source_filename, new_file_content, strlen(new_file_content) }
    };
    unsigned num_unsaved_files = 1;

    // 5. Reparse the CXTranslationUnit
    if (clang_reparseTranslationUnit(
        translation_unit,
        num_unsaved_files,
        unsaved_files,
        CXReparse_None) != 0) {
        fprintf(stderr, "Error: Unable to reparse translation unit. Quitting.\n");
        clang_disposeTranslationUnit(translation_unit);
        clang_disposeIndex(index);
        return -1;
    }

    // 6. Work With the Updated translation_unit
    // (Add your code here to work with the translation_unit if needed)

    // Print a simple message indicating that reparsing was successful
    printf("Reparsing was successful!\n");

    // 7. Clean Up
    clang_disposeTranslationUnit(translation_unit);
    clang_disposeIndex(index);

    return 0;
}

Before running this code, make sure the file example.c exists on your system and is accessible from where you're running the program.

To compile this example you will need to link against libclang. For example, on a Unix-like system with libclang installed, you could compile this program with clang as follows:

clang -o main main.c $(llvm-config --cxxflags --ldflags --libs --system-libs)

Please note, llvm-config is a utility that comes with LLVM that helps to provide the flags necessary for compilation and linking. If your setup does not use llvm-config, you will need to manually specify the paths for libclang headers and link against the libclang library.

This example is greatly simplified and does not do anything with the AST after reparsing. Real applications would typically traverse the AST, perform analysis, refactoring, code completion, or other tasks depending on the use case. Additionally, remember that proper error handling and resource management should be included in production code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment