Предисловие


Существует большое количество статей, посвященных статическим анализаторам для С/С++/С#, Java и т.д. Что касается исследований применения различных статических анализаторов для нативной разработки под MacOS/iOS, то им уделено гораздо меньше внимания.

Предлагается разбор применения статических анализаторов кода, используемых в различных проектах на ObjC и Swift. При этом, это не обзор, а скорее некоторые заметки применения различных инструментов, позволяющих находить ошибки того или иного рода в коде, начиная от утечек памяти и заканчивая поиском уязвимостей. Однако данные не претендуют на объективность и полноту сделанных выводов, а также на глубину анализа полученных результатов по каждому инструменту.

Вступление


Несколько лет назад, ещё будучи начинающим iOS разработчиком, я столкнулся с проблемой верификации качества кода, написанного на ObjC для iOS приложения. К сожалению, не всегда удавалось найти ошибки и обсудить код с более квалифицированными коллегами за неимением таковых. Первые негативные отзывы хоть и воспринимались конструктивно, но, все – таки, приводили к потере клиентов, которые желали оценить примеры кода разработчика перед заключением контракта. В результате этого возникла потребность использования инструментов, выявляющих максимум дефектов кода перед демонстрацией его заказчику. Отмечу, всегда было актуально иметь отчёты о качестве кода в Continuous Integration после всех комитов для выявления потенциальных проблем в разрабатываемом продукте.

Работать приходилось в различных командах с разными проектами от сольных до распределённых, с командой, находящейся на разных континентах. Уровень квалификации коллег варьировался от начального уровня до архитектора. Состав команд аналогично постоянно претерпевал изменения и это при том, что каждый год появлялись новые версии Swift, да и ObjectiveC не стоял на месте.

Все эти факторы стимулировали поиски инструментов, позволяющих выявить максимальное количество ошибок и сделать код более консистентным и легко читаемым.

Clang Static Analyzer


Это первый встроенный анализатор, с которым встречается любой пользователь XCode. Для того, чтобы анализировался код при каждом запуске приложения, необходимо произвести настройки, описанные здесь. Данные рекомендации следует использовать всем пользователям как первую ступень проверки кода перед каждым его коммитом.

Отсутствие поддержки Swift весьма ограничивает области его применения. Конечно, в XCode имеется Address Analyzer и Thread Sanitizer, которые поддерживают Swift – однако эти инструменты не являются статическими анализаторами и могут использоваться только при запуске приложения, то есть в run time.
Подходит для превентивного поиска потенциальных memory-leaks и ошибок в структуре кода.

Faux Pas


Faux Pas (неверный шаг– фр.) — первый сторонний статический анализатор, с которым пришлось столкнуться, имеющий полноценный как GUI так и CLI. После начала его использования качество кода стало заметно выше. Он помог выявить в моих проектах на ObjC от 20% до 50% скрытых баг. Самое курьёзное было то, что инструмент при первом же его запуске выявил именно те ошибки, на которые указал заказчик. Это являлось основным аргументом, почему, собственно, и было решено приобрести данный инструмент за личные средства разработчика, а не ждать пока вся компания примет его на вооружение.

К сожалению, не все коллеги приняли данный инструмент позитивно — некоторые считали, что анализатор должен быть бесплатным, другие — сетовали на большое количество настроек, не желая в них разбираться.

Главный недостаток – это отсутствие поддержки Swift, однако разработчики обещают его поддержку, хоть пока и не анонсируют дату.

Менее значимая проблема — это то, что workspaces поддерживаются не напрямую — проверять приходиться либо через командную строку (конфигурировать проверку workspace), либо по отдельности каждый проект, что, в свою очередь, не всегда удобно.

Тем не менее, Faux Pas находит проблемные участки кода, которые противоречат Apple Coding Guidelines. Например, использование self.propety в init и dealloc, напоминает про использование Modern ObjC syntax, Missing NSNotificationCenter observer detachment и так далее. Легко интегрируется в CI.

Генерируемый отчёт в Json или plist лучше конвертировать в нечто более читаемое. Отчет содержит подробное описание проблемы, в нем также даются внешние ссылки на правила. Несмотря на то, что, временами, попадаются ссылки на stackoverflow, а не на официальную документацию, это, всё – же, не является существенным недостатком.

Пример результата работы инструмента приведен ниже. Для запуска анализатора в XCode, для предметного обнаружения проблем, можно использовать CLI.



Для анализа отчёта, представленного в JSON было написано небольшое приложение, генерирующее отчет в Excel с выборкой по каждой проблеме в представлении в похожем на GUI FauxPas.

Что касается конфигурирования анализатора, то инструмент конфигурируется через:
GUI, Аргументы CLI и конфигурационные файлы. Способа создания собственных правил не обнаружено.

OCLint


Впервые анализатор встретился в небольшом, но долго живущем проекте зарубежной компании написанном на ObjC. Разработчики этой фирмы были весьма консервативны и не желали переходить на Swift, из-за сложности миграции на все новые версии этого языка.

Несмотря на то, что OCLint позволяет менять и добавлять правила через Scaffolding, это, однако, требует много времени. По процессу разработки в данной компании код не будет комититься в репозиторий, пока не исправят все предупреждения анализатора. Некоторые из правил проверки обрабатывались не всегда корректно. Для устранения всех предупреждений анализатора приходилось использовать обходные приемы (костыли), что заставило команду в конечном итоге отказаться от поддержки этого анализатора.

Тем не менее анализатор позволяет выявить:

  • Пустые операторы if/else/try/catch/finally без выражений,
  • Неиспользуемые локальные переменные и параметры
  • Сообщает о высокой цикломатической сложности
  • Реагирует на избыточный код с оператора if и не используемые скобки
  • Позволяет обнаружить инвертируемую логику и переназначение переменных

Время анализа небольшого проекта достаточно велико (по сравнению с Clang анализатором), и если в вашем проекте ранее не применялся данный инструмент, он может выдать огромное количество предупреждений, что затруднит первичный анализ. Поэтому, если желание проверять всю кодовую базу и получать огромный список предупреждений отсутствует, то можно написать небольшой скрипт, который проверяет только изменённые файлы перед комитом, что актуально для любого анализатора.

Стандартно интегрируется в XCode через build Script.

Инструмент достаточно гибкий, но, тем не менее, хотелось бы более продвинутого эвристического анализа кода, так как данный инструмент существует довольно давно. В настоящее время назрела потребность в плагине расширения для XCode этого анализатора, что заметно упростило бы его использование.

Codebeat


Облачный анализатор Codebeat поддерживает как ObjC, так и Swift, что являлось критически важным, когда данный продукт выбирался для использования в конкретном проекте. Одним из недостатков было то, что проверка производилась только после того, как commit попадал на GitHub перед pull request, что делало процесс разработки слегка не структурированным в нашем случае. На момент написания статьи разработчики обещали выпустить утилиту автоматического код ревью для того, чтобы полностью минимизировать участие разработчиков в этом процессе. Это является актуальной задачей, в связи с нерегулярными или невнимательными проверками кода в командах разработчиков в некоторых проектах с человеческим ревью.

Поддерживается Swift + Obj-C, Python и Ruby, что весьма удобно для мобильной разработки.

Анализ ObjC кода работает не вполне удовлетворительно. Это связано с тем, что компания ориентируется в основном на анализ кода на Swift, а работа с ObjC добавлена недавно и требует дальнейшей доработки.

Относительно поддержки продукта, то она на высоте. Всегда была возможность связаться со службой поддержки в течение дня, и даже в выходные предоставлялась необходимая помощь.

Что касается конфигурирования метрик, то это можно сделать, создав свой json файл и изменив правила проверки. Тем не менее, возможность создавать новые правила проверки или модифицировать существующие отсутствует. Однако, как заверили меня разработчики, в будущем такая возможность планируется.

Данный инструмент бесплатен для opensource проектов, а для приватных репозиториев цена не так уж высока, к тому же пробный период длиться 3 месяца, что можно считать достаточно демократичным.

Infer


Продукт facebook, который открыл собственный анализатор кода, подходит не только для анализа проектов на iOS, но и для Android/Java разработки.

Сразу запустить этот анализатор в командной строке для тестового проекта не удалось, но после создания запроса на Github меня посвятили в некоторые тонкости его настройки. Мне даже удалось интегрировать его в XCode с помощью “Run build script”.

Для запуска анализатора была создана дополнительная scheme – Infer в Xcode по аналогии с другими анализаторами, использующими CLI. В частном случае большой пользы от его применения выявлено не было. Возможно, имеет смысл применять данный инструмент в других более крупных кроссплатформенных сложных проектах.

SwiftLint


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

В качестве базововых правил взят GitHub's Swift Style Guide. Существует возможность создания regex-based собственных правил проверки. В частности, очень удобно создавать правила, касающиеся рефакторинга, когда новая функциональность добавляется в расширения классов инструментов, например, функция NSLocalizedString() становиться просто полем в NSString (или String) раширении класса.

В конкретном случае команды работали на распределенных бранчах, на которых не проводился рефакторинг. С помощью опции «autocorrect» можно легко привести код к единому стилю, когда код мержился на единый бранч.

SwiftLint использовался в большом гибридном проекте, содержащем как ObjC, так и Swift код. Автор идеи его внедрения в проект, к сожалению, не прочел статью о превратностях использования — «autocorrect» опции и вставил проверку в «Run Script» в Xcode. Разумеется, в больших проектах с командой, не имеющей общей точки синхронизации, не все установили этот инструмент сразу. Одновременно с этим шла миграция на Swift 3.0, что, несомненно, добавило путаницы при новых коммитах.

Начиная с версии 0.16.0, появились некоторые правила ложного срабатывания, например, c large_tuple проекты перестали компилироваться через build script. Вдобавок не все правила обрабавтываются корректно, что свойственно многим анализаторам и это вынуждает использовать обходные приемы, от которых страдает качество кода. Эта проблема заставила аккуратнее относиться к внесению SwiftLint в “Build phases script” для каждого проекта.

Данный инструмент также был интегрирован в Jenkins, что, очевидно, улучшило контроль над кодовой базой проекта.

Tailor


Tailor это кросплатформенный статический анализатор для Swift. Весьма необычным является тот факт, что поддерживается платформа Windows.

Применялся как альтернатива SwiftLint на пилотных проектах. Однако на данный период времени он поддерживает только Swift 2, что немного не соответствует современным требованиям. Тем не менее существует issue enchantment на поддержку Swift 3.0.1. Поддерживает правила code style для The Swift Programming Language, GitHub, Ray Wenderlich, и Coursera.

Для поддержки pods и workspace рекомендуется использовать следующую рекомендацию.

Возможность создавать собственные правила не обнаружена, хотя вероятность, что она существует, есть, однако в этом следует тщательно разобраться.

Интегрируется в Xcode через “Build Script”. Чтобы подавить предупреждения типа trailing-whitespace, которые сразу начинают доставлять неудобства при первом же его использовании,
Рекомендуется использовать ключи:

tailor --except=brace-style,trailing-whitespace

Отчет при желании можно вывести в HTML, JSON или в формате XCode, что достаточно удобно для применения в CI при публикации результатов на web сервере.

Форматирование в Swift Style


Говоря об анализаторах нельзя не упомянуть о “форматерах” кода для определенного style guide.
Несмотря на то, что они не являются статическим анализаторами кода, а форматируют код к Swift Style, тем не менее, они служат той же цели выявления ошибок путем приведения кода к единому стилю, позволяющей лучше воспринимать код программистом, а, следовательно, быстрее выявлять проблемные участки кода.

SwiftFormat


Удобен тем, что дает возможность создавать собственные правила для удовлетворения code style конкретной команды. Конфигурирование происходит через CLI, что может понравиться далеко не всем. Тем не менее, он может быть установлен как плагин в XCode.

Swimat


Инструмент может быть использован как CLI, так и Xcode extension. Это, действительно, неплохой инструмент, хоть он и содержит несколько критичных баг, которые приводят к падению XCode при его использовании. Однако разработчик оперативно их устраняет. Возможность добавления собственных правил проверки присутствует.

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

Оба форматерра комфортно применять в качестве расширения для XCode, и в настоящее время поддерживаются и улучшаются.

Checkmarx


На практике, временами, встречаются проекты от заказчиков, критично относящихся к безопасности выпускаемого продукта. Для таких проектов лучше воспользоваться Static Application Security Testing (SAST) решением по аудиту исходного кода от компании Checkmarx.

Это облачное решение, то есть инструмент, может располагаться либо на локальном сервере для разработки, либо на сервере компании Checkmarx. Достаточно продвинутый анализатор, использующий собственные запатентованные технологии. У Checkmarx, которая ранее тесно сотрудничала с министерством обороны США, очень много интересных наработок касательно исследований всевозможных уязвимостей. Поддерживает Swift, но в конкретном случае анализировался ObjC проект. К сожалению, не имеет плагина расширения для Xcode.

Система отчетов является очень информативной и презентабельной. Одна из страниц приведена ниже.



Информативный отчет может содержать от 10 до 300 страниц в зависимости от вашего проекта. Также в отчете даются пояснения о том, как исправить проблемы, и даже приводятся примеры. Общей ошибкой для всех проектов, которые подвергались анализу было “Third Party Keyboard Enabled”, что связано с возможной уязвимостью при установке сторонних клавиатур для iOS (правда, одно из анализируемых приложений не содержало полей ввода с клавиатуры).

Послесловие


С некоторыми инструментами статического анализа кода все ещё хотелось бы познакомиться на практике, например, Sonar. Однако из-за дороговизны продукта и сложности его установки такой возможности пока не представилось. В то же время для некоторых проектов интересно попробовать Solar inCode для поиска уязвимостей в коде для заказчиков, волнующихся о безопасности своих приложений. К моему сожалению, этот продукт получить не удалось даже в Demo-версии.

Многие из выше приведенных инструментов помогли сделать создаваемые продукты стабильнее и устойчивее к различным уязвимостям в коде. В принципе, любой анализатор — это непредвзятый судья, которому вы безразличны и у вас нет персональных претензий к нему, что делает всю работу более объективной и упорядоченной.

Тем не менее, очевидно, что идеальных инструментов не бывает, и даже применение все существующих средств анализа кода не является панацеей. Не говоря, о тех случаях ложного срабатывания инструментов когда приходится пользоваться обходными приемами, чтобы удовлетворить требованиям анализатора. В связи с этим, человеческое ревью всегда будет актуально, чтобы хотя бы выявлять смысловые ошибки типа dragState, вместо drugState или cacheData, вместо cashData (правда можно настроить автозамену …).

Полезные ссылки


Поделиться с друзьями
-->

Комментарии (18)


  1. Andrey2008
    16.02.2017 21:00

    И тишина…

    Видимо следующий анализатор, нам всё же стоит делать не для Objective-C, а скажем для Java. :)


    1. evgzor
      16.02.2017 21:13

      Для Swift будет больше инструментов появляться, если язык будет дальше развиваться. Все зависит от востребованности языка.


  1. KvanTTT
    16.02.2017 21:46
    +2

    В свете этого топика не лишним будет упомянуть и о веб-сервисе Swiftify по преобразованию Objective-C кода в Swift.


    Кстати, Codebeat и Swiftify используют ANTLR и открытую Objective-C грамматику. Однако подходы парсинга немного различаются.


    Анализ ObjC кода работает не вполне удовлетворительно. Это связано с тем, что компания ориентируется в основном на анализ кода на Swift, а работа с ObjC добавлена недавно и требует дальнейшей доработки.

    Ну да, летом вышеупомянутая грамматика была похуже качеством, чем сейчас =) (я занимаюсь ее разработкой). И в целом обработка препроцесосрных директив в C подобных языках — дело непростое.


    1. awoland
      17.02.2017 10:36

      А не знаете, случайно, сервисов или приложений для обратного преобразования кода (Swift > ObjC)?


      1. KvanTTT
        17.02.2017 10:55

        Не знаю. А зачем такое нужно? Ведь ObjC более старый и менее удобный язык.


        1. awoland
          17.02.2017 11:17

          На вкус и цвет все карандаши…


          1. KvanTTT
            17.02.2017 11:30
            -1

            Если такой реализовывать, то конвертинг Swift -> ObjC реализовать попроще.


            1. s_suhanov
              17.02.2017 13:49

              Конвертните плиз в ObjC такое:


              guard let info = result["info"] as? [String : AnyObject] else {
                  return
              }
              
              print("\(info["title"])")


              1. ivlevAstef
                17.02.2017 18:31
                +1

                хм…


                NSDictionary* info = result["info"]
                if (![info isKindOfClass: NSDictionary.class]) {
                  return
                }
                NSLog("%@", info["title"])

                Вроде так?
                Вот enum свифта перевести в enum Obj-C, куда посложнее задача, если первый используется не только как обычное перечисление.


                1. DnV
                  17.02.2017 20:16
                  +1

                  Тогда ещё нужно проверку типов, например, к словарю категорией прикрутить. В общем случае будет выглядеть как-то так:

                  if (![info isKindOfClass:[NSDictionary class]] || ![info validateKeysClass:[NSString class] andValuesClass:[NSObject class]]) {
                  


              1. KvanTTT
                17.02.2017 21:43

                Ну я говорил про общий случай. В Swift, например, очень ограниченная поддержка препроцессорных директив, что упрощает процесс.


                1. Crulex
                  21.02.2017 03:42
                  +1

                  Действительно, обратная конвертация Swift -> Obj-C может оказаться не проще, если учитывать поддержку сложных языковых конструкций Swift, включая optionals.
                  Сложный Swift enum можно представить в виде класса в Obj-C.
                  Переобразование enum values по словарю примерно одинаково по сложности с переобразованием Obj-C -> Swift.
                  Действительно, меня многие спрашивают про обратный конвертор Swift -> ObjC, и вероятно такой у нас в перспективе будет.
                  Единственный вопрос в том, что Obj-C используется все меньше.
                  Буду благоларен если приведете use cases для чего именно вам нужен такой конвертор (я основатель Swiftify).


  1. corristo
    17.02.2017 10:56
    +1

    В форматерах было бы здорово упомянуть clang-format.


    1. evgzor
      17.02.2017 10:59

      Упустил. Спасибо за ценный коментарий.


  1. aknew
    17.02.2017 14:34
    +1

    Может не совсем в кассу, но я бы еще упомянул следующие (увы, все для obj-c):
    https://github.com/nst/objc_dep — строит граф зависимостей фаилов, можно оценить насколько все связано друг с другом в визуальной форме, а иногда и обнаружить что у тебя есть фаилы стоящие отдельно и ни с чем ни линкуемые
    http://uncrustify.sourceforge.net/ — форматер, довольно известный и универсальный (работает с C, C++, C#, ObjectiveC, D, Java, Pawn и VALA)
    https://pmd.github.io/pmd-5.4.1/usage/cpd-usage.html — поиск дублирующего кода, тоже для кучи всего, но есть и поиск для obj-c. Раньше он был через сторонний плагин, теперь похоже прямо в ванильной версии (каюсь — не пробовал, как-то раньше обходился старой версией, а в последнее время не использовал)

    Ну и есть еще такой вопрос (который я уже пару раз задавал, но ответа пока не получил) — а вы проверяли как приведенные вами анализаторы ведут себя с тем же тайфуном? По моим наблюдениям, кланг попросту перестает проверять то что подключено через тайфун т.к. не видит свзяи между классами (оно и понятно — там линкуется-то только протокол, сам класс уже в рантайме оказывается связан).


    1. evgzor
      17.02.2017 14:46

      Спасибо за ссылки — обязательно попробую. C проектами использующие тайфун не приходилось встречаться, к сожалению. Возможно имеет смысл в таких случаях исполльзовать run-time анализатор.


      1. aknew
        17.02.2017 14:52

        Рантайм это Instruments или valgrind подобные? Они же вроде не могут сами перебирать разные варианты что было бы если код пойдет по такой ветке if-а, а кланг может причем делает это между различными методами


        1. evgzor
          17.02.2017 15:15

          По dynamic analysis tools подробных исследований не проводил. Но вопрос очень хороший — постараюсь его проработать.