Получить информацию о директивах препроцессора

Недавно я начал использовать libclang для анализа файлов C. Проблема, с которой я сталкиваюсь, заключается в том, что, по-видимому, libclang инициирует препроцессор перед созданием AST. Я хотел бы запретить запуск препроцессора и вместо этого получить информацию о том, что директивы препроцессора находятся в файле...

Я использую следующий скрипт Python (cindex.py и libclang)

import codecs
from clang.cindex import *

class SourceFile(object):
    def __init__(self, path):
        with codecs.open(path, 'r', 'utf-8') as file:
            self.file_content = file.read()

        index = Index.create()
        root_node = index.parse(path)

        for included in root_node.get_includes():
            print included.include

        self.print_declerations(root_node.cursor)

    def print_declerations(self, root, recurse=True):
        print root.kind.name, root.spelling
        if root.kind.is_declaration():
            node_def = root.get_definition()
            if node_def is not None:
                start_offset = node_def.extent.start.offset
                end_offset = node_def.extent.end.offset + 1
                print self.file_content[start_offset:end_offset], '\n'

        if recurse:
            for child in root.get_children():
                self.print_declerations(child, False)

if __name__ == '__main__':
    path = 'Sample.cpp'
    print 'Translation unit:', path
    source = SourceFile(path)

Какие выходы

Translation unit: Sample.cpp
/mingw/include\stdio.h
/mingw/include\_mingw.h
/mingw/include\sys/types.h
TRANSLATION_UNIT None
TYPEDEF_DECL __builtin_va_list

STRUCT_DECL _iobuf

TYPEDEF_DECL FILE

VAR_DECL _iob
UNEXPOSED_DECL 

FUNCTION_DECL main
int main()
{
    printf(HELLO_WORLD);
    return 0;
}

Для следующего C-кода:

#include <stdio.h>
#define HELLO_WORLD "HELLO!"

int main()
{
    printf(HELLO_WORLD);
    return 0;
}

Я хотел бы получить DEFINE_DECL HELLO_WORLD для моего #define в коде (в настоящее время я ничего не получаю). И, конечно же, получите аналогичные утверждения для моих #include. Это возможно?

РЕДАКТИРОВАТЬ: В принципе, я хочу проанализировать файл без расширенных директив препроцессора.


person Robin Heggelund Hansen    schedule 14.12.2012    source источник
comment
Если вы готовы рассмотреть что-то кроме Clang, у меня есть альтернативное решение.   -  person Ira Baxter    schedule 31.12.2012


Ответы (3)


Несколько дней назад я задал тот же вопрос на irc-канале #llvm freenode. Ответ был «макросы не являются частью AST, поэтому вы не можете», но, скорее всего, вам может помочь опция «-fsyntax-only» и плагин clang вместо libclang.

Отредактировано: похоже, теперь это действительно возможно, см. ответ bradtgmurray

person Alexander Smirnov    schedule 30.12.2012
comment
Вы имеете в виду написать свой собственный плагин clang? или есть плагин, который на самом деле называется clang plugin? Не самая простая фраза для использования в Google. - person Robin Heggelund Hansen; 02.01.2013
comment
Проверьте «clang» и загляните в папку «examples». Для начала можно использовать PrintFunctionNames и плагин-анализатор. - person Alexander Smirnov; 05.01.2013

Если вы добавите PARSE_DETAILED_PROCESSING_RECORD в качестве опции к вашему вызову index.parse(), вы получите доступ к узлам препроцессора.

index = clang.cindex.Index.create()                                                                         
tu = index.parse(filename, options=clang.cindex.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD)

Этот параметр соответствует следующему значению параметра libclang C API. Там есть комментарий, который включает в себя дополнительный контекст.

/**                                                                         
 * \brief Used to indicate that the parser should construct a "detailed"    
 * preprocessing record, including all macro definitions and instantiations.
 *                                                                          
 * Constructing a detailed preprocessing record requires more memory        
 * and time to parse, since the information contained in the record         
 * is usually not retained. However, it can be useful for                   
 * applications that require more detailed information about the            
 * behavior of the preprocessor.                                            
 */                                                                         
CXTranslationUnit_DetailedPreprocessingRecord = 0x01,   
person bradtgmurray    schedule 08.08.2013
comment
Сегодня тоже нашел. Вернитесь сюда, чтобы добавить новый ответ, но вот он. Нечего добавить. - person Alexander Smirnov; 08.09.2013
comment
Обратите внимание, что документация неверна. Он включает не все экземпляры макроса, а только те, которые сами не входят в состав экземпляра макроса (таким образом, отслеживается только один уровень раскрытия макроса). Вы можете закомментировать if в начале PreprocessingRecord::addMacroExpansion, чтобы изменить это. - person Cameron; 19.05.2016
comment
_DetailedPreprocessingRecord приводит к обнаружению всех макросов (даже тех, которые должны были быть проигнорированы, поскольку они определяются условием препроцессора #ifdef _BLAH \n #define _FLAG \n #endif. Как я могу отфильтровать только релевантные? - person Bhavneet Singh Bajwa; 06.06.2020
comment
^^^ Хорошо для тех, кто еще интересуется, что происходит, если вы проанализируете с флагом CXTranslationUnit_SingleFileParse, вы получите доступ ко всем макросам (не удаляя мой предыдущий комментарий, может оказаться полезным для кого-то). - person Bhavneet Singh Bajwa; 06.06.2020

Если вы используете аргументы командной строки для вызова libclang, вот соответствующий код из реализации C API libclang:

// Do we need the detailed preprocessing record?
if (options & CXTranslationUnit_DetailedPreprocessingRecord) {
  Args->push_back("-Xclang");
  Args->push_back("-detailed-preprocessing-record");
}
person andrewrk    schedule 29.01.2016