PVS-Studio - это инструмент для выявления ошибок в исходном коде программ, написанных на языках С, C++ и C#.
PVS-Studio выполняет статический анализ кода и генерирует отчёт, помогающий программисту находить и устранять ошибки. PVS-Studio выполняет широкий спектр проверок кода, но наиболее силён в поисках опечаток и последствий неудачного Copy-Paste. Показательные примеры таких ошибок: V501, V517, V522, V523, V3001.
Анализатор ориентирован на разработчиков, использующих среду Visual Studio, и может в фоновом режиме выполнять анализ измененных файлов после их компиляции. В идеале ошибки будут обнаружены и исправлены ещё до попадания в репозиторий. Однако ничто не мешает использовать анализатор для проверки всего решения целиком или для встраивания в системы непрерывной интеграции. Эти и иные способы использования анализатора описаны в документации.
PVS-Studio выполняет статический анализ кода и генерирует отчёт, помогающий программисту находить и устранять ошибки. PVS-Studio выполняет широкий спектр проверок кода, но наиболее силён в поисках опечаток и последствий неудачного Copy-Paste. Показательные примеры таких ошибок: V501, V517, V522, V523, V3001.
Анализатор ориентирован на разработчиков, использующих среду Visual Studio, и может в фоновом режиме выполнять анализ измененных файлов после их компиляции. В идеале ошибки будут обнаружены и исправлены ещё до попадания в репозиторий. Однако ничто не мешает использовать анализатор для проверки всего решения целиком или для встраивания в системы непрерывной интеграции. Эти и иные способы использования анализатора описаны в документации.
Введение в Roslyn. Использование для разработки инструментов статического анализа. Часть 1
Запись от el_programmer размещена 19.05.2016 в 17:23
Показов 3482
Комментарии 0
|
Автор: Сергей Васильев Roslyn является платформой, предоставляющей разработчику различные мощные средства для разбора и анализа кода. Но наличия таких средств недостаточно, нужно понимать, что и для чего необходимо использовать. Данная статья несёт цель ответить на подобные вопросы. Помимо этого, будет рассказано об особенностях разработки статических анализаторов, использующих Roslyn API. Введение Знания, приведённые в данной статье, получены при разработке статического анализатора кода PVS-Studio, часть которого, отвечающая за проверку C#-проектов, написана с использованием Roslyn API. Статью можно разделить на 2 больших логических раздела:
Если же разбить статью на разделы более детально, можно выделить следующие части:
Примечание. Дополнительно предлагаю вашему вниманию родственную статью "Руководство по разработке модулей расширений на C# для Visual Studio 2005-2012 и Atmel Studio". Roslyn Roslyn – платформа с открытым исходным кодом, разрабатываемая корпорацией Microsoft, и содержащая в себе компиляторы и средства для разбора и анализа кода, написанного на языках программирования C# и Visual Basic. Roslyn используется в среде разработки Microsoft Visual Studio 2015. Различные нововведения наподобие code fixes реализуются как раз за счёт использования Roslyn. С помощью средств анализа, предоставляемыми платформой Roslyn, можно производить полный разбор кода, анализируя все поддерживаемые конструкции языка. Среда Visual Studio позволяет создавать на основе Roslyn как встраиваемые в саму IDE инструменты (расширения Visual Studio), так и независимые приложения (standalone инструменты). Исходный код Roslyn доступен в соответствующем репозитории на GitHub. Это позволяет посмотреть, что и как работает, а в случае обнаружения какой-либо ошибки – сообщить о ней разработчикам. Рассматриваемый ниже вариант создания статического анализатора и диагностических правил является не единственным. Возможно создание диагностик, основанное на использовании стандартного класса DiagnosticAnalyzer. Встроенные диагностики Roslyn используют именно это решение. Это позволит, например, произвести интеграцию со стандартным списком ошибок Visual Studio, предоставляет возможность подсветки ошибок в текстовом редакторе и т.д. Но стоит помнить, что если эти диагностики будут существовать внутри процесса devenv.exe, являющегося 32-битным, накладываются серьёзные ограничения на объём используемой памяти. В некоторых случаях это критично и не позволит провести глубокий анализ больших проектов (того же Roslyn). К тому же в этом случае Roslyn оставляет разработчику меньше контроля по обходу дерева и самостоятельно занимается распараллеливанием этого процесса. C# анализатор PVS-Studio является standalone-приложением, что решает проблему с ограничением на использование памяти. Помимо этого, мы получаем больший контроль над обходом дерева, реализуем распараллеливание необходимым нам образом, тем самым больше контролируя процесс разбора и анализа кода. Так как опыт в создании анализатора, работающего по такому принципу (PVS-Studio С++), уже есть, его было бы целесообразно использовать и при написании C# анализатора. Интеграция со средой разработки Visual Studio осуществляется аналогично C++ анализатору – посредством плагина, вызывающего это standalone-приложение. Таким образом, используя уже имеющиеся наработки, удалось создать анализатор для нового языка и связать его с уже имеющимися решениями, встроив в полноценный продукт – PVS-Studio. Подготовка к анализу файлов Перед тем, как приступать к самому анализу, необходимо получить список файлов, исходный код которых будет проверяться, а также получить сущности, необходимые для корректного анализа. Можно выделить несколько пунктов, которые нужно выполнить для получения необходимых для анализа данных:
На каждом пункте стоит остановиться чуть подробнее. Создание рабочего пространства Создание рабочего пространства (workspace) необходимо для получения решения или проектов. Для получения workspace'a необходимо вызвать статический метод Create класса MSBuildWorkspace, возвращающий объект типа MSBuildWorkspace. Получение решения Получение solution'a актуально, когда необходимо проанализировать, например, несколько входящих в данное решение проектов, или их все. Тогда, получив solution, легко можно получить список всех входящих в него проектов. Для получения solution'a используется метод OpenSolutionAsync объекта MSBuildWorkspace. В итоге получаем коллекцию, содержащая в себе список проектов (т.е. объект IEnumerable<Project>). Получение проектов Если отсутствует необходимость в анализе всех проектов, можно получить конкретный, интересующий нас проект, используя асинхронный метод OpenProjectAsync объекта MSBuildWorkspace. Используя этот метод, получаем объект типа Project. Разбор проекта: получение компиляции и списка файлов для анализа После того, как получен список проектов для анализа, можно приступать к их разбору. Результатом разбора проекта должен стать список файлов для анализа и компиляция. Список файлов получить просто – для этого используется свойство Documents экземпляра класса Project. Для получения компиляции используется метод TryGetCompilation или GetCompilationAsync. Получение компиляции – один из ключевых моментов, так как именно она используется для получения семантической модели (подробнее о которой будет рассказано позже), необходимой для проведения глубокого и сложного анализа исходного кода. Для того, чтобы получить корректную компиляцию, проект должен быть скомпилированным – в нём не должно быть ошибок компиляции, а все зависимости должны лежать на месте. Пример использования. Получение проектов Ниже приведён код, демонстрирующий различные варианты получения проектных файлов с использованием класса MSBuildWorkspace:
Разбор файла: получение синтаксического дерева и семантической модели Следующий этап – разбор файла. Сейчас необходимо получить 2 сущности, на которых и базируется полноценный анализ – синтаксическое дерево и семантическую модель. Синтаксическое дерево строится на основании исходного кода программы и используется для анализа различных конструкций языка. Семантическая модель предоставляет информацию об объектах и их типах. Для получения синтаксического дерева (объект типа SyntaxTree) используется метод TryGetSyntaxTree или GetSyntaxTreeAsync экземпляра класса Document. Семантическая модель (объект типа SemanticModel) получается из компиляции с использованием синтаксического дерева, полученного ранее. Для этого используется метод GetSemanticModel экземпляра класса Compilation, принимающего в качестве обязательного параметра объект типа SyntaxTree. Класс, который будет обходить синтаксическое дерево и проводить анализ, должен быть унаследован от класса CSharpSyntaxWalker, что позволит переопределить методы обхода различных узлов. Вызывая метод Visit, принимающий в качестве параметра корень дерева (для его получения используется метод GetRoot объекта типа SyntaxTree), мы тем самым запускаем рекурсивный обход узлов синтаксического дерева. Ниже приведён код, в котором демонстрируется реализация описанных выше действий:
Переопределённые методы обхода узлов Для каждой конструкции языка определены узлы своего типа. А для каждого типа узла определён метод, выполняющих обход узлов подобного типа. Таким образом, добавляя обработчики (диагностические правила) в методы обхода тех или иных узлов, мы можем анализировать только интересующие нас конструкции языка. Пример переопределённого метода обхода узлов, соответствующих оператору if:
Синтаксическое дерево Синтаксическое дерево является базовым элементом, необходимым для анализа кода. Именно по нему происходит перемещение в ходе анализа. Дерево строится на основе кода, приведённого в файле, из чего следует вывод, что каждый файл имеет своё синтаксическое дерево. Помимо этого стоит учитывать тот факт, что синтаксическое дерево является неизменяемым. Нет, изменить его, конечно, можно, вызвав соответствующий метод, но результатом его работы будет новое синтаксическое дерево, а не изменённое старое. Например, для следующего кода:
Здесь синим цветом обозначены узлы дерева (Syntax nodes), а зелёным – лексемы (Syntax tokens). В синтаксическом дереве, которое строит Roslyn на основе программного кода, можно выделить 3 элемента:
Каждый из этих элементов дерева стоит рассмотреть подробнее, так как все они так или иначе используются в ходе статического анализа. Другое дело, что одни из них используются регулярно, а другие – на порядок реже. Syntax nodes Syntax nodes (далее – узлы) представляют синтаксические конструкции, такие как объявления, операторы, выражения и т.д. Основная работа, происходящая при анализе кода, приходится на обработку узлов. Именно по ним происходит перемещение, на обходе тех или иных видов узлов базируются диагностические правила. Рассмотрим пример дерева, соответствующего выражению
Базовый тип Базовым типом узлов является абстрактный класс SyntaxNode. Этот класс предоставляет в распоряжение разработчика методы, общие для всех узлов. Перечислим некоторые наиболее часто используемые из них (если какие-то вещи, вроде того, что такое SyntaxKind или т.п. будут вам сейчас непонятны – не волнуйтесь, об этом будет рассказано ниже):
Помимо этого в классе определен ряд свойств. Некоторые из них:
Производные типы Но вернёмся к типам узлов. Каждый узел, представляющий ту или иную конструкцию языка, имеет свой тип, определяющий ряд свойств, упрощающих навигацию по дереву и получение необходимых данных. Этих типов – множество. Приведём некоторые из них и то, каким конструкциям языка они соответствуют:
Пример. Разбор оператора if Рассмотрим пример того, как использовать эти знания на практике на примере оператора if. Пусть в анализируемом коде есть фрагмент следующего вида:
На практике это выглядит так же, как и в теории:
Конкретизация типа узла. Перечисление SyntaxKind Порой бывает недостаточно знать тип узла. Один из случаев – префиксные операции. Например, нам нужно выделить префиксные операции инкремента и декремента. Можно было бы проверить тип узла.
На помощь приходит перечисление SyntaxKind. В этом перечислении определены все возможные конструкции языка, а также его ключевые слова, модификаторы и пр. С помощью элементов этого перечисления можно установить конкретный тип узла. Для конкретизации типа узла в классе SyntaxNode определены следующие свойства и методы:
Используя методы Kind или IsKind, можно легко определить, является ли узел префиксной операцией инкремента или декремента:
Syntax tokens Syntax tokens (далее – лексемы) являются терминалами грамматики языка. Лексемы представляют собой элементы, которые не подлежат дальнейшему разбору – идентификаторы, ключевые слова, специальные символы. В ходе анализа кода напрямую с ними приходится работать куда реже, чем с узлами дерева. Однако если всё же приходится работать с лексемами, как правило, это ограничивается получением текстового представления лексемы или же проверки её типа. Рассмотрим упоминавшееся ранее выражение.
Использование при анализе Все лексемы представлены значимым типом SyntaxToken. Поэтому для того, чтобы узнать, чем же именно является лексема, используются упоминавшиеся ранее методы Kind и IsKind и элементы перечисления SyntaxKind. Если же необходимо получить текстовое представление лексемы, достаточно обратиться к свойству ValueText. Можно получить и значение лексемы (например, число, если лексема представлена числовым литералом), для чего достаточно обратиться к свойству Value, возвращающему ссылку типа Object. Однако для получения константных значений обычно применяется семантическая модель и более удобный метод GetConstantValue, который будет рассмотрен в соответствующем разделе. Кроме того, к лексемам (фактически – к ним, а не к узлам) привязаны syntax trivia (о том, что это, написано в следующем разделе). Для работы с syntax trivia определены следующие свойства:
Пример использования Рассмотрим простой оператор if:
Пример получения значений лексемы:
Syntax trivia Syntax trivia (дополнительная синтаксическая информация) – это те элементы дерева, которые не будут скомпилированы в IL-код. К таким элементам относятся элементы форматирования (пробелы, символы перевода строки), комментарии, директивы препроцессора. Рассмотрим простое выражение следующего вида:
Использование при анализе Дополнительная синтаксическая информация, как упоминалось ранее, связана с лексемами. Разделяют Leading trivia и Trailing trivia. Leading trivia – предшествующая лексеме дополнительная синтаксическая информация, trailing trivia – дополнительная синтаксическая информация, следующая за лексемой. Все элементы дополнительной синтаксической информации имеют тип SyntaxTrivia. Для определения того, чем конкретно является элемент (пробел, однострочный комментарий, многострочный комментарий и пр.) используется перечисление SyntaxKind и уже известные вам методы Kind и IsKind. Как правило, при статическом анализе вся работа с дополнительной синтаксической информацией сводится к определению того, чем является её элементы, иногда – к анализу текста элемента. Пример использования Пусть у нас имеется следующий анализируемый код:
Если в качестве узла нам приходит выражение a = b;, легко получить текст однострочного и многострочного комментариев следующим образом:
Краткое обобщение Кратко обобщив информацию данного раздела, можно выделить следующие пункты, касаемо синтаксического дерева:
Продолжение: https://www.cyberforum.ru/blog... g4267.html | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии


