Я расширяю программный инструмент для расчета метрик для программных проектов.
Метрики затем используются для статического анализа кода.
Моя задача - реализовать расчет метрик для проектов c и c ++.
В процессе разработки я столкнулся с проблемами, которые привели к сбросу и повторному запуску с другим инструментом или языком программирования.
Я изложу процесс, проблемы и вещи, которые я пытался решить, в хронологическом порядке и как можно лучше.
Некоторые метрики:
- Строки кода для классов, структур, объединений, функций / методов и исходных файлов
- Количество методов для классов и структур
- Сложность для классов, структур и функций / методов
- Зависимости для / между классами и структурами
Поскольку c ++ является сложным языком для разбора, а написание синтаксического анализатора c ++ самостоятельно не соответствует масштабу, я склонен использовать существующий синтаксический анализатор c ++.
Поэтому я начал использовать библиотеки из LLVM Project для сбора синтаксической и семантической информации об исходном файле.
LLVM Tooling link: https://clang.llvm.org/docs/Tooling.html
Сначала Я начал с LibTooling, написанного на c ++, так как он обещал мне «полный контроль» над абстрактным синтаксическим деревом (AST).
Я попробовал RecursiveASTVistor и Matchfinder , но безуспешно.
Итак, LibTooling был отклонен, потому что я не смог получить контекстную информацию об окружении узла в AST.
Я смог отреагировать только на обратный вызов, когда посетил конкретный узел в AST. Но я не знал, в каком контексте я в настоящее время был.
Например. Когда я посещал C ++ RecordDeclaration (класс, структура, объединение), я не знал, является ли это вложенной записью или нет.
Но эта информация необходима для вычисления строк кода для одного класса.
Второй подход заключался в использовании интерфейса LibClang через привязки Python.
Благодаря интерфейсу LibClang я смог рекурсивно обходить узел AST и хранить необходимую информацию о контексте в стеке.
Здесь я столкнулся с общей проблемой с LibClang:
Перед созданием AST для файла запускается препроцессор и разрешает все директивы препроцессора. Так же, как он должен делать.
- Это хорошо, потому что если препроцессор не может разрешить все директивы включения, выходной AST будет неполным.
- Это очень плохо, потому что я не смогу предоставить все включаемые файлы или каталоги для любого проекта c ++.
- Это плохо, потому что код, который окружен условными директивами препроцессора, не является частью AST, если переменная препроцессора определена или нет. Многократный анализ одного и того же файла с различными настройками определенной или неопределенной переменной препроцессора находится вне области действия.
Это привело к третьей и текущей попытке использования синтаксического анализатора c ++, сгенерированного Antlr , обеспечивающего грамматику c ++ 14 .
Препроцессор перед парсером не выполняется.
Это хорошо, потому что полный исходный код анализируется, а директивы препроцессора игнорируются.
Плохо то, что парсер не кажется таким уж жестким. Это терпит неудачу на коде, который может быть скомпилирован, приводя к сломанному AST. Так что этого решения недостаточно.
Мои вопросы:
- Есть ли возможность деактивировать препроцессор перед синтаксическим анализом исходного или заголовочного файла c / c ++ с помощью libClang?
Таким образом, исходный код не тронут, а AST полон и подробен.
- Есть ли способ проанализировать файл исходного кода c / c ++ без предоставления всех необходимых каталогов include, но при этом получить подробный AST?
- Так как у меня заканчиваются варианты. На какие еще подходы стоит обратить внимание при анализе / разборе исходного кода на языке c / c ++?
Если вы считаете, что это неправильное место, чтобы задавать такие вопросы, не стесняйтесь перенаправить меня в другое место.