Diese Präsentation wurde erfolgreich gemeldet.
Die SlideShare-Präsentation wird heruntergeladen. ×

Rambler.iOS #9: Анализируй это!

Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige

Hier ansehen

1 von 107 Anzeige

Rambler.iOS #9: Анализируй это!

Herunterladen, um offline zu lesen

Rambler.iOS #9: Анализируй это! (Сергей Крапивенский).

Доклад посвящён наиболее популярным статическим анализаторам кода для iOS: как ими пользоваться, какие проблемы они решают, как внедрить их в привычный цикл разработки, как писать для них свои правила. Также рассмотрен опыт интеграции статического анализа и CI.

Rambler.iOS - митапы iOS-разработчиков, организуемые компанией RAMBLER&Co.

Rambler.iOS #9: Анализируй это! (Сергей Крапивенский).

Доклад посвящён наиболее популярным статическим анализаторам кода для iOS: как ими пользоваться, какие проблемы они решают, как внедрить их в привычный цикл разработки, как писать для них свои правила. Также рассмотрен опыт интеграции статического анализа и CI.

Rambler.iOS - митапы iOS-разработчиков, организуемые компанией RAMBLER&Co.

Anzeige
Anzeige

Weitere Verwandte Inhalte

Diashows für Sie (19)

Ähnlich wie Rambler.iOS #9: Анализируй это! (20)

Anzeige

Weitere von RAMBLER&Co (20)

Aktuellste (20)

Anzeige

Rambler.iOS #9: Анализируй это!

  1. 1. Анализируй это Сергей Крапивенский, RamblerDigitalSolutions
  2. 2. Чем занимаются разработчики? 2 • Проектируют архитектуру • Оценивают сроки • Пишут код и тесты • Ловят БАГИ !
  3. 3. Ожидание
  4. 4. Реальность
  5. 5. Отлов багов заранее 5 Компилятор Предупреждения Тесты
  6. 6. Отлов багов заранее 6 Ревью Статический анализ
  7. 7. План 7 Статический анализ 101 Принцип работы на примере OCLint Правила и их кастомизация Внедрение анализа в CI
  8. 8. План 8 Статический анализ 101 Принцип работы на примере OCLint Правила и их кастомизация Внедрение анализа в CI
  9. 9. План 9 Статический анализ 101 Принцип работы на примере OCLint Правила и их кастомизация Внедрение анализа в CI
  10. 10. План 10 Статический анализ 101 Принцип работы на примере OCLint Правила и их кастомизация Внедрение анализа в CI
  11. 11. Что находят анализаторы? 11 • Мерзкие неочевидные ошибки • Ненужный код • Слишком запутанный код • Code smells
  12. 12. Как они работают? 12 bool isPositive(int i) { if (i > 0) { return true; } else { return false; } }
  13. 13. 13 bool isPositive(int i) { if (i > 0) { Function If Statement Operator > true false If Body 0i Else Body returnreturn return true; } else { return false; } }
  14. 14. 13 bool isPositive(int i) { if (i > 0) { Function If Statement Operator > true false If Body 0i Else Body returnreturn return true; } else { return false; } }
  15. 15. 13 bool isPositive(int i) { if (i > 0) { Function If Statement Operator > true false If Body 0i Else Body returnreturn return true; } else { return false; } }
  16. 16. 13 bool isPositive(int i) { if (i > 0) { Function If Statement Operator > true false If Body 0i Else Body returnreturn return true; } else { return false; } }
  17. 17. 13 bool isPositive(int i) { if (i > 0) { Function If Statement Operator > true false If Body 0i Else Body returnreturn return true; } else { return false; } }
  18. 18. Мой первый раз
  19. 19. 15
  20. 20. 16
  21. 21. 17
  22. 22. Clang static analyzer 18
  23. 23. OCLint vs clang analyzer 19 • Больше правил • Включение/выключение правил • Кастомизация правил • Выбор формата отчета
  24. 24. Схема работы 20
  25. 25. 21 1. Компиляция проекта 2. Сборка флагов компиляции xcodebuild -> xcodebuild.log
  26. 26. Compilation database 22 { "directory": “/Users/serkrapiv/MobiusLint", "command": "../clang -x objective-c -arch armv7 -std=gnu99", "file": "/Users/serkrapiv/MobiusLint/file.cpp" }
  27. 27. 23 1. Компиляция проекта xcodebuild -> xcodebuild.log 2. Сборка флагов компиляции xcodebuild.log + xcpretty -> compilation_database.json 3. Непосредственно анализ oclint-json-compilation-database -> отчет
  28. 28. 24 Результат
  29. 29. 25
  30. 30. Правила 26 мёртвый код пустые if и циклы bad practices ObjC 2.0 избыточность стиль isEqual, нет hash super не вызван C++ и кастомные
  31. 31. 27 RuleBase CodeReader Visitor ASTVisitor ASTMatcher
  32. 32. 27 RuleBase CodeReader Visitor ASTVisitor ASTMatcher
  33. 33. 27 RuleBase CodeReader Visitor ASTVisitor ASTMatcher
  34. 34. 27 RuleBase CodeReader Visitor ASTVisitor ASTMatcher
  35. 35. 27 RuleBase CodeReader Visitor ASTVisitor ASTMatcher
  36. 36. SourceCodeReader 28
  37. 37. 29 class LongLineRule : public AbstractSourceCodeReaderRule { public: virtual const string name() const override { return "long line"; } virtual int priority() const override { return 3; } virtual const string category() const override { return "size"; }
  38. 38. 29 virtual const string category() const override { return "size"; } #ifdef DOCGEN virtual const std::string since() const override { return "0.6"; } virtual const std::string description() const override { return "When the number of characters for one line of code is very high, " "it largely harms the readability. Break long lines of code into multiple lines."; }
  39. 39. 29 code is very high, " "it largely harms the readability. Break long lines of code into multiple lines."; } virtual const std::string example() const override { return R"rst( .. code-block:: cpp void example() { int a012345678901234567890123456789...12345678901234567890123456789 01234567890123456789; } )rst"; }
  40. 40. 30 int threshold = RuleConfiguration::intForKey("LONG_LINE", 100); virtual void eachLine(int lineNumber, string line) override { } }; static RuleSet rules(new LongLineRule()); int currentLineSize = line.size(); if (currentLineSize > threshold) { string description = "Line with " + toString<int>(currentLineSize) + " characters exceeds limit of " + toString<int>(threshold); addViolation(lineNumber, 1, lineNumber, currentLineSize, this, description); }
  41. 41. 30 int threshold = RuleConfiguration::intForKey("LONG_LINE", 100); virtual void eachLine(int lineNumber, string line) override { } }; static RuleSet rules(new LongLineRule()); int currentLineSize = line.size(); if (currentLineSize > threshold) { string description = "Line with " + toString<int>(currentLineSize) + " characters exceeds limit of " + toString<int>(threshold); addViolation(lineNumber, 1, lineNumber, currentLineSize, this, description); }
  42. 42. 30 int threshold = RuleConfiguration::intForKey("LONG_LINE", 100); virtual void eachLine(int lineNumber, string line) override { } }; static RuleSet rules(new LongLineRule()); int currentLineSize = line.size(); if (currentLineSize > threshold) { string description = "Line with " + toString<int>(currentLineSize) + " characters exceeds limit of " + toString<int>(threshold); addViolation(lineNumber, 1, lineNumber, currentLineSize, this, description); }
  43. 43. 30 int threshold = RuleConfiguration::intForKey("LONG_LINE", 100); virtual void eachLine(int lineNumber, string line) override { } }; static RuleSet rules(new LongLineRule()); int currentLineSize = line.size(); if (currentLineSize > threshold) { string description = "Line with " + toString<int>(currentLineSize) + " characters exceeds limit of " + toString<int>(threshold); addViolation(lineNumber, 1, lineNumber, currentLineSize, this, description); }
  44. 44. 30 int threshold = RuleConfiguration::intForKey("LONG_LINE", 100); virtual void eachLine(int lineNumber, string line) override { } }; static RuleSet rules(new LongLineRule()); int currentLineSize = line.size(); if (currentLineSize > threshold) { string description = "Line with " + toString<int>(currentLineSize) + " characters exceeds limit of " + toString<int>(threshold); addViolation(lineNumber, 1, lineNumber, currentLineSize, this, description); }
  45. 45. ASTVisitor 31
  46. 46. 32 virtual void setUp() override {} virtual void tearDown() override {} /* Visit IfStmt bool VisitIfStmt(IfStmt *node) { return true; } /* Visit ObjCSelectorExpr bool VisitObjCSelectorExpr(ObjCSelectorExpr *node) { return true; } */
  47. 47. 32 virtual void setUp() override {} virtual void tearDown() override {} /* Visit IfStmt bool VisitIfStmt(IfStmt *node) { return true; } /* Visit ObjCSelectorExpr bool VisitObjCSelectorExpr(ObjCSelectorExpr *node) { return true; } */
  48. 48. 32 virtual void setUp() override {} virtual void tearDown() override {} /* Visit IfStmt bool VisitIfStmt(IfStmt *node) { return true; } /* Visit ObjCSelectorExpr bool VisitObjCSelectorExpr(ObjCSelectorExpr *node) { return true; } */
  49. 49. ASTMatcher 33
  50. 50. 34 virtual void setUpMatcher() override { // Описываем паттерн и добавляем матчеры } virtual void callback(const MatchFinder::MatchResult &result) override { // Обрабатываем полученные результаты }
  51. 51. 34 virtual void setUpMatcher() override { // Описываем паттерн и добавляем матчеры } virtual void callback(const MatchFinder::MatchResult &result) override { // Обрабатываем полученные результаты }
  52. 52. Кастомизация 35
  53. 53. ./scaffoldRule -t <Type> -n <Name> -c <Category> -p <Priority> 36 RuleName.cpp в /oclint-rules/rules RuleNameTest.cpp в /oclint-rules/test ./build rules libRuleName.dylib в /build/oclint-rules/rules.dl Копируем библиотеку в /build/oclint-release/lib/oclint/rules
  54. 54. 37 virtual void eachLine(int lineNumber, string line) override { } "
  55. 55. 38 virtual void eachLine(int lineNumber, string line) override { } virtual void eachLine(int lineNumber, string line) override { if (line.size() > 0) { char c = line.at(0); } virtual void eachLine(int lineNumber, string line) override { if (c == '{') { addViolation(lineNumber, 0, lineNumber, 1, this, "Opening bracket misplacement"); } } }
  56. 56. 38 virtual void eachLine(int lineNumber, string line) override { } virtual void eachLine(int lineNumber, string line) override { if (line.size() > 0) { char c = line.at(0); } virtual void eachLine(int lineNumber, string line) override { if (c == '{') { addViolation(lineNumber, 0, lineNumber, 1, this, "Opening bracket misplacement"); } } }
  57. 57. 38 virtual void eachLine(int lineNumber, string line) override { } virtual void eachLine(int lineNumber, string line) override { if (line.size() > 0) { char c = line.at(0); } virtual void eachLine(int lineNumber, string line) override { if (c == '{') { addViolation(lineNumber, 0, lineNumber, 1, this, "Opening bracket misplacement"); } } }
  58. 58. ASTVisitor - идеи # 39 @property (nonatomic, strong copy) NSArray *arr; @property (nonatomic, assign weak) id delegate;
  59. 59. ASTVisitor - идеи # 39 @property (nonatomic, strong copy) NSArray *arr; @property (nonatomic, assign weak) id delegate;
  60. 60. ASTVisitor - идеи # 39 @property (nonatomic, strong copy) NSArray *arr; @property (nonatomic, assign weak) id delegate;
  61. 61. ASTVisitor 40 bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *node) { return true; } bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *node) { return true; } bool VisitObjCPropertyDecl(ObjCPropertyDecl *node) { return true; }
  62. 62. ASTVisitor 41 bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *node) { return true; } bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *node) { return true; } bool VisitObjCPropertyDecl(ObjCPropertyDecl *node) { return true; }
  63. 63. ASTVisitor 42 bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *node) { return true; } bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *node) { return true; } bool VisitObjCPropertyDecl(ObjCPropertyDecl *node) { return true; } $
  64. 64. ASTVisitor 42 bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *node) { return true; } bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *node) { return true; } bool VisitObjCPropertyDecl(ObjCPropertyDecl *node) { return true; } $
  65. 65. 43 bool VisitObjCPropertyDecl(ObjCPropertyDecl *node) { return true; } string description = ...; addViolation(node, this, description); } if (hasMutableSubclass(node) && hasStrongAttribute(node)) {
  66. 66. 43 bool VisitObjCPropertyDecl(ObjCPropertyDecl *node) { return true; } string description = ...; addViolation(node, this, description); } if (hasMutableSubclass(node) && hasStrongAttribute(node)) {
  67. 67. 43 bool VisitObjCPropertyDecl(ObjCPropertyDecl *node) { return true; } string description = ...; addViolation(node, this, description); } if (hasMutableSubclass(node) && hasStrongAttribute(node)) {
  68. 68. 44 bool hasMutableSubclass(ObjCPropertyDecl *propertyDecl) { } auto TypePtr = propertyDecl->getType().getTypePtr(); if (auto InterfaceType = TypePtr->getAsObjCInterfacePointerType()) { } return false; } string objClassName = InterfaceType->getObjectType()- >getBaseType().getAsString(); std::vector<string> classesNames = {"NSArray", "NSString", "NSDictionary", "NSSet"}; return std::find(classesNames.begin(), classesNames.end(), objClassName) != classesNames.end();
  69. 69. 44 bool hasMutableSubclass(ObjCPropertyDecl *propertyDecl) { } auto TypePtr = propertyDecl->getType().getTypePtr(); if (auto InterfaceType = TypePtr->getAsObjCInterfacePointerType()) { } return false; } string objClassName = InterfaceType->getObjectType()- >getBaseType().getAsString(); std::vector<string> classesNames = {"NSArray", "NSString", "NSDictionary", "NSSet"}; return std::find(classesNames.begin(), classesNames.end(), objClassName) != classesNames.end();
  70. 70. 44 bool hasMutableSubclass(ObjCPropertyDecl *propertyDecl) { } auto TypePtr = propertyDecl->getType().getTypePtr(); if (auto InterfaceType = TypePtr->getAsObjCInterfacePointerType()) { } return false; } string objClassName = InterfaceType->getObjectType()- >getBaseType().getAsString(); std::vector<string> classesNames = {"NSArray", "NSString", "NSDictionary", "NSSet"}; return std::find(classesNames.begin(), classesNames.end(), objClassName) != classesNames.end();
  71. 71. 44 bool hasMutableSubclass(ObjCPropertyDecl *propertyDecl) { } auto TypePtr = propertyDecl->getType().getTypePtr(); if (auto InterfaceType = TypePtr->getAsObjCInterfacePointerType()) { } return false; } string objClassName = InterfaceType->getObjectType()- >getBaseType().getAsString(); std::vector<string> classesNames = {"NSArray", "NSString", "NSDictionary", "NSSet"}; return std::find(classesNames.begin(), classesNames.end(), objClassName) != classesNames.end();
  72. 72. 44 bool hasMutableSubclass(ObjCPropertyDecl *propertyDecl) { } auto TypePtr = propertyDecl->getType().getTypePtr(); if (auto InterfaceType = TypePtr->getAsObjCInterfacePointerType()) { } return false; } string objClassName = InterfaceType->getObjectType()- >getBaseType().getAsString(); std::vector<string> classesNames = {"NSArray", "NSString", "NSDictionary", "NSSet"}; return std::find(classesNames.begin(), classesNames.end(), objClassName) != classesNames.end();
  73. 73. 45 bool hasStrongAttribute(ObjCPropertyDecl *propertyDecl) { } return propertyDecl->getPropertyAttributes() } & ObjCPropertyDecl::OBJC_PR_strong; }
  74. 74. 45 bool hasStrongAttribute(ObjCPropertyDecl *propertyDecl) { } return propertyDecl->getPropertyAttributes() } & ObjCPropertyDecl::OBJC_PR_strong; }
  75. 75. 45 bool hasStrongAttribute(ObjCPropertyDecl *propertyDecl) { } return propertyDecl->getPropertyAttributes() } & ObjCPropertyDecl::OBJC_PR_strong; }
  76. 76. ASTMatcher 46 int result = a > b ? x = c > d ? c : d : y; %
  77. 77. ASTMatcher 47 virtual void setUpMatcher() override { } anyOf( hasTrueExpression(nestedMatcher), hasFalseExpression(nestedMatcher))) StatementMatcher nestedMatcher = expr(hasDescendant(conditionalOperator())); addMatcher( .bind("nested")); conditionalOperator(
  78. 78. ASTMatcher 47 virtual void setUpMatcher() override { } anyOf( hasTrueExpression(nestedMatcher), hasFalseExpression(nestedMatcher))) StatementMatcher nestedMatcher = expr(hasDescendant(conditionalOperator())); addMatcher( .bind("nested")); conditionalOperator(
  79. 79. ASTMatcher 47 virtual void setUpMatcher() override { } anyOf( hasTrueExpression(nestedMatcher), hasFalseExpression(nestedMatcher))) StatementMatcher nestedMatcher = expr(hasDescendant(conditionalOperator())); addMatcher( .bind("nested")); conditionalOperator(
  80. 80. ASTMatcher 47 virtual void setUpMatcher() override { } anyOf( hasTrueExpression(nestedMatcher), hasFalseExpression(nestedMatcher))) StatementMatcher nestedMatcher = expr(hasDescendant(conditionalOperator())); addMatcher( .bind("nested")); conditionalOperator(
  81. 81. ASTMatcher 47 virtual void setUpMatcher() override { } anyOf( hasTrueExpression(nestedMatcher), hasFalseExpression(nestedMatcher))) StatementMatcher nestedMatcher = expr(hasDescendant(conditionalOperator())); addMatcher( .bind("nested")); conditionalOperator(
  82. 82. ASTMatcher 48 virtual void callback(const MatchFinder::MatchResult &result) override { } const ConditionalOperator *conditionalOperator = result.Nodes.getNodeAs<ConditionalOperator>("nested"); if (conditionalOperator) { addViolation(conditionalOperator->getCond(), this); }
  83. 83. ASTMatcher 48 virtual void callback(const MatchFinder::MatchResult &result) override { } const ConditionalOperator *conditionalOperator = result.Nodes.getNodeAs<ConditionalOperator>("nested"); if (conditionalOperator) { addViolation(conditionalOperator->getCond(), this); }
  84. 84. ASTMatcher 48 virtual void callback(const MatchFinder::MatchResult &result) override { } const ConditionalOperator *conditionalOperator = result.Nodes.getNodeAs<ConditionalOperator>("nested"); if (conditionalOperator) { addViolation(conditionalOperator->getCond(), this); }
  85. 85. Мем с джеки чаном
  86. 86. clang -Xclang -ast-dump -fsyntax-only myFile.m 50 CompoundStmt `-IfStmt |-BinaryOperator | |-ImplicitCastExpr | | `-DeclRefExpr | `-IntegerLiteral |-CompoundStmt | `-ReturnStmt | `-CXXBoolLiteralExpr `-CompoundStmt `-ReturnStmt `-CXXBoolLiteralExpr
  87. 87. http://clang.llvm.org/docs/LibASTMatchersReference.html 51
  88. 88. Автоматизация 52
  89. 89. 53
  90. 90. 54 https://github.com/rambler-ios/fastlane-flows
  91. 91. 55rules: - MutableObjectPropertyWithStrongAttribute - PointerTypePropertyWithAssignAttribute rule-configurations: - key: LONG_LINE value: 200 exclude_regex: /Pods/ report_type: json
  92. 92. Ну такое& 56
  93. 93. Геймификация! 57
  94. 94. 61
  95. 95. Формула здоровья 62 if crashfree > 98 crashfree_coefficient = (0.7 * crashfree - 68.6)/2 + 0.3 else crashfree_coefficient = crashfree * 0.003 / 0.9 end
  96. 96. Формула здоровья 62 if crashfree > 98 crashfree_coefficient = (0.7 * crashfree - 68.6)/2 + 0.3 else crashfree_coefficient = crashfree * 0.003 / 0.9 end
  97. 97. 63
  98. 98. 63
  99. 99. Ретроспектива 64 Меньше багов, чище код Работа на примере clang и OCLint Возможности и кастомизация Автоматизация и геймификация
  100. 100. Ретроспектива 65 Меньше багов, чище код Работа на примере clang и OCLint Возможности и кастомизация Автоматизация и геймификация
  101. 101. Ретроспектива 66 Меньше багов, чище код Работа на примере clang и OCLint Возможности и кастомизация Автоматизация и геймификация
  102. 102. Ретроспектива 67 Меньше багов, чище код Работа на примере clang и OCLint Возможности и кастомизация Автоматизация и геймификация
  103. 103. Статическому анализу да! ' 68
  104. 104. Спасибо! serkrapiv sergey.krapivenskiy rambler-ios

×