Как обработать исходный код c и c ++ для вычисления метрик для статического анализа кода? - PullRequest
3 голосов
/ 21 марта 2019

Я расширяю программный инструмент для расчета метрик для программных проектов. Метрики затем используются для статического анализа кода. Моя задача - реализовать расчет метрик для проектов 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 ++?

Если вы считаете, что это неправильное место, чтобы задавать такие вопросы, не стесняйтесь перенаправить меня в другое место.

...