├── .gitignore ├── CMakeLists.txt ├── README.md ├── Sherlock.cpp ├── Sherlock.exports ├── Sherlock.hpp ├── SherlockConfigure.hpp ├── SherlockInitDesign.cpp ├── SherlockInitDesign.hpp ├── SherlockObservePairing.cpp ├── SherlockObservePairing.hpp ├── SherlockParser.cpp ├── SherlockParser.hpp ├── SherlockTest ├── AFViewController.h ├── AFViewController.m ├── Even_Wrong_More_ViewController.h ├── Even_Wrong_More_ViewController.m ├── NSString+AFSherlock.h ├── NSString+AFSherlock.m ├── NSString+Sherlock.h ├── NSString+Sherlock.m ├── SherlockViewController.h ├── SherlockViewController.m ├── ThirdPart │ ├── ThirdPartViewController.h │ └── ThirdPartViewController.m ├── WrongNameViewController.h └── WrongNameViewController.m └── theRule.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (Sherlock) 3 | 4 | set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) 5 | set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) 6 | set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) 7 | 8 | set( LLVM_HOME /.../compiler/llvm ) 9 | set( LLVM_SRC_DIR ${LLVM_HOME}/llvm ) 10 | set( CLANG_SRC_DIR ${LLVM_HOME}/llvm/tools/clang ) 11 | set( LLVM_BUILD_DIR ${LLVM_HOME}/llvm_build ) 12 | set( CLANG_BUILD_DIR ${LLVM_HOME}/llvm_build/tools/clang) 13 | 14 | add_definitions (-D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS) 15 | add_definitions (-D_GNU_SOURCE -DHAVE_CLANG_CONFIG_H) 16 | 17 | set (CMAKE_CXX_COMPILER "${LLVM_BUILD_DIR}/bin/clang++") 18 | set (CMAKE_CC_COMPILER "${LLVM_BUILD_DIR}/bin/clang") 19 | 20 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} 21 | -fPIC 22 | -fno-common 23 | -Woverloaded-virtual 24 | -Wcast-qual 25 | -fno-strict-aliasing 26 | -pedantic 27 | -Wno-long-long 28 | -Wall 29 | -Wno-unused-parameter 30 | -Wwrite-strings 31 | -fno-exceptions 32 | -fno-rtti" 33 | ) 34 | 35 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11") 36 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") 37 | 38 | set (CMAKE_MODULE_LINKER_FLAGS "-Wl,-flat_namespace -Wl,-undefined -Wl,suppress") 39 | 40 | set (LLVM_LIBS 41 | LLVMX86CodeGen 42 | LLVMX86AsmParser 43 | LLVMX86Disassembler 44 | LLVMExecutionEngine 45 | LLVMAsmPrinter 46 | LLVMSelectionDAG 47 | LLVMX86AsmPrinter 48 | LLVMX86Info 49 | LLVMMCParser 50 | LLVMCodeGen 51 | LLVMX86Utils 52 | LLVMScalarOpts 53 | LLVMInstCombine 54 | LLVMTransformUtils 55 | LLVMAnalysis 56 | LLVMTarget 57 | LLVMCore 58 | LLVMMC 59 | LLVMSupport 60 | LLVMBitReader 61 | LLVMOption 62 | LLVMProfileData 63 | ) 64 | 65 | macro(add_clang_plugin name) 66 | set (srcs ${ARGN}) 67 | 68 | include_directories( "${LLVM_SRC_DIR}/include" 69 | "${CLANG_SRC_DIR}/include" 70 | "${LLVM_BUILD_DIR}/include" 71 | "${CLANG_BUILD_DIR}/include" ) 72 | link_directories( "${LLVM_BUILD_DIR}/lib" ) 73 | 74 | add_library( ${name} SHARED ${srcs} ) 75 | 76 | if (SYMBOL_FILE) 77 | set_target_properties( ${name} PROPERTIES LINK_FlAGS 78 | "-exported_symbols_list ${SYMBOL_FILE}") 79 | endif() 80 | 81 | foreach (clang_lib ${CLANG_LIBS}) 82 | target_link_libraries( ${name} ${clang_lib} ) 83 | endforeach() 84 | 85 | foreach (llvm_lib ${LLVM_LIBS}) 86 | target_link_libraries( ${name} ${llvm_lib} ) 87 | endforeach() 88 | 89 | foreach (user_lib ${USER_LIBS}) 90 | target_link_libraries( ${name} ${user_lib} ) 91 | endforeach() 92 | 93 | endmacro(add_clang_plugin) 94 | 95 | set(SYMBOL_FILE Sherlock.exports) 96 | 97 | set (CLANG_LIBS 98 | clang 99 | clangFrontend 100 | clangAST 101 | clangAnalysis 102 | clangBasic 103 | clangCodeGen 104 | clangDriver 105 | clangFrontendTool 106 | clangLex 107 | clangParse 108 | clangSema 109 | clangEdit 110 | clangSerialization 111 | clangStaticAnalyzerCheckers 112 | clangStaticAnalyzerCore 113 | clangStaticAnalyzerFrontend 114 | ) 115 | 116 | set (USER_LIBS 117 | pthread 118 | curses 119 | z 120 | ) 121 | 122 | add_clang_plugin(Sherlock 123 | SherlockConfigure.hpp 124 | Sherlock.hpp 125 | Sherlock.cpp 126 | SherlockInitDesign.hpp 127 | SherlockInitDesign.cpp 128 | SherlockObservePairing.hpp 129 | SherlockObservePairing.cpp 130 | SherlockParser.hpp 131 | SherlockParser.cpp 132 | ) 133 | 134 | set_target_properties(Sherlock PROPERTIES 135 | LINKER_LANGUAGE CXX 136 | PREFIX "") 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Sherlock 2 | 3 | > Yep, This is "the detective", the one with a magnifying glass. 4 | 5 | Sherlock是一个有高拓展性的代码规范检查基础工具,您的团队可以在Sherlock的基础上,轻松做定制、拓展。Sherlock是一个C++写的Clang Plugin,面向检查Objective-C代码。注意,使用Sherlock前必须要先将Clang Plugin整合到Xcode中,如何整合Clang Plugin和更多详细内容点[这里](http://blog.mrriddler.com/2017/02/24/Clang%E6%8F%92%E4%BB%B6-Sherlock/)。项目中的SherlockTest是测试代码,将Clang Plugin整合好后,可以创建新工程跑测试代码。 6 | 7 | #### buildIn base rule 8 | 9 | 首先看看Sherlock提供的基本规则,首先是给Warning的: 10 | 11 | - interface_illegal_character:项目文件命名中有"_"。 12 | - project_prefix:项目中间接或直接继承自UIVIewController的controller命名没有以XX开头。 13 | - property_atomic:属性声明为atomic。 14 | - property_unsafe:属性声明为unsafe_unretained。 15 | - property_copy:NSArray、NSNumber、NSString、block类型属性声明不为copy。 16 | - weak_protocol:protocol类型的属性声明不为weak。 17 | - category_method_naming:给系统类添加的Category方法命名没有以XX_开头。 18 | - initializer_design:类没有用Designated Initializer设计init初始化方法。 19 | 20 | 然后是直接给Error的: 21 | 22 | - mutable_property_copy:可变数据类型属性声明为copy。(copy会制作一个不可变数据深复制) 23 | - weak_proxy:调用NSTimer或CADisplayLink方法,并将self作为参数传入。(容易引起循环引用,推荐使用WeakProxy) 24 | - super_call:调用[super respondsToSelector:]或[super conformsToProtocol:]。(这样调用,等价于检查自己是否实现,没意义) 25 | - observe_pairing:在一个编译单位中,添加NSNotificationCenter或KVO的观察者和移除观察者的操作数量不配对。(忘记移除观察者) 26 | 27 | #### Configure 28 | 29 | 使用Sherlock前你需要在SherlockConfigure.hpp文件中修改ProjectAbsoluteRootPath为项目的根路径。然后,可以提供一个脚本的路径给RuleAbsolutePath字段,在脚本中进行Configure。现在支持的Configure有以下几项: 30 | 31 | - disabled_rules:将规则名称以"-"开头,紧跟空格,如- initializer_design,可以关掉相应的规则。 32 | - force_cast:可以键入Error或Warning,强制所有rule报告错误或是警告。 33 | - project_prefix:键入比如Sherlock,提供项目的前缀名。 34 | - blacklist_path:键入比如- Pods,忽略所有项目中Pods目录下的编译单元。 35 | - blacklist_prefix:键入比如- AF,忽略所有Interface、Extension、implementation、Category以AF作为前缀的代码。 36 | 37 | 注意,所有以- 开头代表的多个选项必须以另起一行~end结束。样例Configure文件如下: 38 | 39 | ```shell 40 | disabled_rules: 41 | - interface_illegal_character 42 | - project_prefix 43 | - property_atomic 44 | - property_unsafe 45 | - property_copy 46 | - weak_protocol 47 | - category_method_naming 48 | - initializer_design 49 | - mutable_property_copy 50 | - weak_proxy 51 | - super_call 52 | - observe_pairing 53 | ~end 54 | 55 | force_report: Warning 56 | 57 | project_prefix: Sherlock 58 | 59 | blacklist_path: 60 | - Pods 61 | - ThirdPart 62 | ~end 63 | 64 | blacklist_prefix: 65 | - AF 66 | - SD 67 | - FM 68 | - YY 69 | ~end 70 | ``` 71 | 72 | 还有一点需注意,如果你想忽略所有Pod下的第三方编译单元,只在blacklist_path中加入- Pods是行不通的。预处理的时候,会将引入编译单元合并,任意编译单元引入第三方,都会导致第三方编译单元被检查。如果想要完全忽略第三方编译单元要在blacklist_prefix加入前缀。 73 | 74 | 75 | 76 | #### CustomRule 77 | 78 | Sherlock提供了良好的拓展性,但是你还是需要一个clang的AST知识才能编写检查规则代码。添加一条新规则有如下几步: 79 | 80 | - 为新的规则取一个名字,与原有规则不能重名,名字将作为规则的识别。然后加入到SherlockConfigure.hpp文件中的buildInBaseRules结构里。 81 | - 在Sherlock.cpp创建一个新方法编写检查规则代码,在调用该方法的地方调用isBuildInBaseRuleEnable方法查看是否被关闭。 82 | 83 | 如果有更多的自定义Configure,从SherlockParser.hpp中的tupleRuleMap或setRuleMap就可以找到解析出来的字符串。 84 | 85 | -------------------------------------------------------------------------------- /Sherlock.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Sherlock.cpp 3 | // Sherlock 4 | // 5 | // Created by Robbie on 2017/1/29. 6 | // 7 | // 8 | 9 | #include "Sherlock.hpp" 10 | #include "clang/AST/ASTContext.h" 11 | #include "SherlockConfigure.hpp" 12 | #include "SherlockParser.hpp" 13 | 14 | static bool isInBlackList; 15 | 16 | namespace Sherlock 17 | { 18 | void SherlockASTVisitor::setContext(ASTContext &context) 19 | { 20 | this->context = &context; 21 | } 22 | 23 | bool SherlockASTVisitor::VisitStmt(Stmt *S) 24 | { 25 | if (isInBlackList) { 26 | return true; 27 | } 28 | 29 | ObjCMessageExpr *ME = dyn_cast_or_null(S); 30 | if (ME) { 31 | if (this->isBuildInBaseRuleEnable(10)) { 32 | this->checkSuperMethod(ME); 33 | } 34 | 35 | if (this->isBuildInBaseRuleEnable(9)) { 36 | this->checkTimerAndDisplayer(ME); 37 | } 38 | } 39 | 40 | return true; 41 | } 42 | 43 | bool SherlockASTVisitor::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID) 44 | { 45 | if (this->checkIsSourceInBlackList(ID->getLocStart(), ID->getNameAsString())) { 46 | isInBlackList = true; 47 | return true; 48 | } 49 | 50 | isInBlackList = false; 51 | if (this->isBuildInBaseRuleEnable(0)) { 52 | this->checkInterfaceIllegalCharacters(ID); 53 | } 54 | 55 | if (this->isBuildInBaseRuleEnable(1)) { 56 | this->checkInterfacePrefix(ID); 57 | } 58 | return true; 59 | } 60 | 61 | bool SherlockASTVisitor::VisitObjCImplementationDecl(ObjCImplementationDecl *IMPD) 62 | { 63 | if (this->checkIsSourceInBlackList(IMPD->getLocStart(), IMPD->getNameAsString())) { 64 | isInBlackList = true; 65 | return true; 66 | } 67 | 68 | isInBlackList = false; 69 | if (this->isBuildInBaseRuleEnable(11)) { 70 | this->checkObservePairing(IMPD); 71 | } 72 | return true; 73 | } 74 | 75 | bool SherlockASTVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) 76 | { 77 | if (CD->IsClassExtension()) { 78 | ObjCInterfaceDecl *ID = CD->getClassInterface(); 79 | if (ID) { 80 | if (this->checkIsSourceInBlackList(CD->getLocStart(), ID->getNameAsString())) { 81 | isInBlackList = true; 82 | return true; 83 | } 84 | 85 | isInBlackList = false; 86 | } 87 | } else if (this->checkIsSourceInBlackList(CD->getLocStart(), CD->getNameAsString())) { 88 | return true; 89 | } 90 | 91 | if (this->isBuildInBaseRuleEnable(6)) { 92 | this->checkCategoryMethodNaming(CD); 93 | } 94 | return true; 95 | } 96 | 97 | bool SherlockASTVisitor::VisitObjCPropertyDecl(ObjCPropertyDecl *PD) 98 | { 99 | if (isInBlackList) { 100 | return true; 101 | } 102 | 103 | string typeStr = PD->getType().getAsString(); 104 | ObjCPropertyDecl::PropertyAttributeKind attKin = PD->getPropertyAttributes(); 105 | 106 | if (this->isBuildInBaseRuleEnable(4)) { 107 | this->checkCopyProperty(PD, typeStr, attKin); 108 | } 109 | 110 | if (this->isBuildInBaseRuleEnable(8)) { 111 | this->checkMutableProperty(PD, typeStr, attKin); 112 | } 113 | 114 | if (this->isBuildInBaseRuleEnable(5)) { 115 | this->checkDelegateProperty(PD, typeStr, attKin); 116 | } 117 | if (this->isBuildInBaseRuleEnable(2)) { 118 | this->checkAtomicProperty(PD, typeStr, attKin); 119 | } 120 | 121 | if (this->isBuildInBaseRuleEnable(3)) { 122 | this->checkUnsafeProperty(PD, typeStr, attKin); 123 | } 124 | 125 | return true; 126 | } 127 | 128 | bool SherlockASTVisitor::VisitObjCMethodDecl(ObjCMethodDecl *MD) 129 | { 130 | if (isInBlackList) { 131 | return true; 132 | } 133 | 134 | if (this->isBuildInBaseRuleEnable(7)) { 135 | this->checkInitDesign(MD); 136 | } 137 | 138 | return true; 139 | } 140 | 141 | void SherlockASTVisitor::checkInterfaceIllegalCharacters(ObjCInterfaceDecl *ID) 142 | { 143 | string name = ID->getNameAsString(); 144 | 145 | if (name.find("_") != string::npos) { 146 | SourceLocation SL = ID->getLocation(); 147 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 148 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Warning), "Interface Naming should not contain _"); 149 | diagEngine.Report(SL, diagId); 150 | } 151 | } 152 | 153 | void SherlockASTVisitor::checkInterfacePrefix(ObjCInterfaceDecl *ID) 154 | { 155 | if (!isSubclassOf("UIViewController", ID)) { 156 | return; 157 | } 158 | 159 | string name = ID->getNameAsString(); 160 | string rule = buildInTupleRules[1]; 161 | map::iterator ite = tupleRuleMap.find(rule); 162 | if (ite != tupleRuleMap.end()) { 163 | string prefix = ite->second; 164 | 165 | if (name.compare(0, prefix.length(), prefix)) { 166 | SourceLocation SL = ID->getLocation(); 167 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 168 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Warning), "Interface Naming should start with project prefix"); 169 | diagEngine.Report(SL, diagId); 170 | } 171 | } 172 | } 173 | 174 | void SherlockASTVisitor::checkCopyProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin) 175 | { 176 | if (!(attKin & ObjCPropertyDecl::OBJC_PR_copy) 177 | && (typeStr.find("NSArray") != string::npos 178 | || typeStr.find("NSString") != string::npos 179 | || typeStr.find("NSNumber") != string::npos 180 | || PD->getType()->isBlockPointerType())) { 181 | SourceLocation location = PD->getSourceRange().getBegin(); 182 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 183 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Warning), "Property should be declared as Copy instead"); 184 | diagEngine.Report(location, diagId); 185 | } 186 | } 187 | 188 | void SherlockASTVisitor::checkMutableProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin) 189 | { 190 | if (typeStr.find("NSMutable") == string::npos) { 191 | return; 192 | } 193 | 194 | if (attKin & ObjCPropertyDecl::OBJC_PR_copy) { 195 | SourceLocation location = PD->getSourceRange().getBegin(); 196 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 197 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Error), "Mutable Object should not be declared as Copy"); 198 | diagEngine.Report(location, diagId); 199 | } 200 | } 201 | 202 | void SherlockASTVisitor::checkDelegateProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin) 203 | { 204 | if (typeStr.find("id<") != string::npos 205 | && typeStr.find(">") != string::npos 206 | && !(attKin & ObjCPropertyDecl::OBJC_PR_weak)) { 207 | SourceLocation location = PD->getSourceRange().getBegin(); 208 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 209 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Warning), "Delegate should be declared as weak instead"); 210 | diagEngine.Report(location, diagId); 211 | } 212 | } 213 | 214 | void SherlockASTVisitor::checkAtomicProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin) 215 | { 216 | if (attKin & ObjCPropertyDecl::OBJC_PR_atomic) { 217 | SourceLocation location = PD->getSourceRange().getBegin(); 218 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 219 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Warning), "pthread_mutex_t And dispatch_semaphore_t have a better performance, you should consider twice"); 220 | diagEngine.Report(location, diagId); 221 | } 222 | } 223 | 224 | void SherlockASTVisitor::checkUnsafeProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin) 225 | { 226 | if (attKin & ObjCPropertyDecl::OBJC_PR_unsafe_unretained 227 | && !(attKin & ObjCPropertyDecl::OBJC_PR_assign)) { 228 | SourceLocation location = PD->getSourceRange().getBegin(); 229 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 230 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Warning), "unsafe_unretained though have better performance than weak, may lead wild pointer, please consider twice"); 231 | diagEngine.Report(location, diagId); 232 | } 233 | } 234 | 235 | void SherlockASTVisitor::checkCategoryMethodNaming(ObjCCategoryDecl *CD) 236 | { 237 | if (CD->IsClassExtension()) { 238 | return; 239 | } 240 | 241 | string IDStr = CD->getClassInterface()->getNameAsString(); 242 | if (IDStr.find("NS") == string::npos) { 243 | return; 244 | } 245 | 246 | for (ObjCContainerDecl::method_iterator ite = CD->meth_begin(); ite != CD->meth_end(); ++ite) { 247 | ObjCMethodDecl *MD = *ite; 248 | string selStr = MD->getSelector().getAsString(); 249 | if (selStr.find("_") == string::npos) { 250 | SourceLocation location = MD->getSourceRange().getBegin(); 251 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 252 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Warning), "Cocoa Foundation Category method naming should start with XX_"); 253 | diagEngine.Report(location, diagId); 254 | } 255 | } 256 | } 257 | 258 | void SherlockASTVisitor::checkInitDesign(ObjCMethodDecl *MD) 259 | { 260 | SherlockInitDesign initDes; 261 | 262 | string selStr = MD->getSelector().getAsString(); 263 | 264 | if (selStr.find("init") != string::npos 265 | && selStr.find("initWithCoder") == string::npos 266 | && selStr.find("initWithNibName") == string::npos) { 267 | if (MD->hasBody()) { 268 | initDes.TraverseStmt(MD->getBody()); 269 | 270 | if (!initDes.isDesignatedInitializer()) { 271 | SourceLocation location = MD->getSourceRange().getBegin(); 272 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 273 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Warning), "multiple init method should provide Designated Initializer"); 274 | diagEngine.Report(location, diagId); 275 | } 276 | } 277 | } 278 | } 279 | 280 | void SherlockASTVisitor::checkObservePairing(ObjCImplementationDecl *IMPD) 281 | { 282 | SherlockObservePairing notPair; 283 | 284 | for (ObjCContainerDecl::method_iterator ite = IMPD->meth_begin(); ite != IMPD->meth_end(); ++ite) { 285 | ObjCMethodDecl *MD = *ite; 286 | if (MD->hasBody()) { 287 | notPair.TraverseStmt(MD->getBody()); 288 | } 289 | } 290 | 291 | if (notPair.isNotificationMissPairing()) { 292 | SourceLocation location = IMPD->getSourceRange().getBegin(); 293 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 294 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Error), "Add and Remove Observer for Notification are not pairing"); 295 | diagEngine.Report(location, diagId); 296 | } 297 | 298 | if (notPair.isKVOMissPairing()) { 299 | SourceLocation location = IMPD->getSourceRange().getBegin(); 300 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 301 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Error), "Add and Remove Observer for KVO are not pairing"); 302 | diagEngine.Report(location, diagId); 303 | } 304 | } 305 | 306 | void SherlockASTVisitor::checkSuperMethod(ObjCMessageExpr *ME) 307 | { 308 | const ObjCMessageExpr::ReceiverKind recek = ME->getReceiverKind(); 309 | if (recek == ObjCMessageExpr::SuperInstance) { 310 | 311 | ObjCMethodDecl *MD = ME->getMethodDecl(); 312 | string selStr = MD->getNameAsString(); 313 | if (selStr.find("respondsToSelector") != string::npos 314 | || selStr.find("conformsToProtocol") != string::npos) { 315 | SourceLocation location = ME->getSourceRange().getBegin(); 316 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 317 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Error), "call super respondsToSelector or conformsToProtocol method only lead to check current class"); 318 | diagEngine.Report(location, diagId); 319 | } 320 | } 321 | } 322 | 323 | void SherlockASTVisitor::checkTimerAndDisplayer(ObjCMessageExpr *ME) 324 | { 325 | ObjCInterfaceDecl *IE = ME->getReceiverInterface(); 326 | if (IE) { 327 | StringRef declName = IE->getName(); 328 | if (declName.equals(StringRef("NSTimer")) 329 | || declName.equals(StringRef("CADisplayLink"))) { 330 | for (ObjCMessageExpr::arg_iterator ite = ME->arg_begin(); ite != ME->arg_end(); ++ite) { 331 | Expr *E = *ite; 332 | if (E->isObjCSelfExpr()) { 333 | SourceLocation location = ME->getSourceRange().getBegin(); 334 | DiagnosticsEngine &diagEngine = this->context->getDiagnostics(); 335 | unsigned diagId = diagEngine.getCustomDiagID(this->decideReportType(DiagnosticsEngine::Error), "NSTimer or CADisplayLink method invoke with self as parameter lead to retain cycle, should use weakProxy instead"); 336 | diagEngine.Report(location, diagId); 337 | } 338 | } 339 | } 340 | } 341 | } 342 | 343 | bool SherlockASTVisitor::checkIsSourceInBlackList(SourceLocation SL, string name) 344 | { 345 | if (this->checkIsInSystemHeader(SL)) { 346 | return true; 347 | } 348 | 349 | if (this->checkIsInBlackListPath(SL)) { 350 | return true; 351 | } 352 | 353 | if (this->checkIsNameInBlackList(SL, name)) { 354 | return true; 355 | } 356 | 357 | return false; 358 | } 359 | 360 | bool SherlockASTVisitor::checkIsInBlackListPath(SourceLocation SL) 361 | { 362 | const FileEntry *fileEntry = this->context->getSourceManager().getFileEntryForID(this->context->getSourceManager().getFileID(SL)); 363 | 364 | StringRef filePath = fileEntry->tryGetRealPathName(); 365 | 366 | if (filePath.startswith(ProjectAbsoluteRootPath)) { 367 | string filePathStr = filePath.str(); 368 | 369 | string relativePath = filePathStr.erase(0, ProjectAbsoluteRootPath.length()); 370 | 371 | string blackListPath = buildInSetRules[2]; 372 | map>::iterator ite = setRuleMap.find(blackListPath); 373 | set blackSet; 374 | if (ite == setRuleMap.end()) { 375 | return false; 376 | } else { 377 | blackSet = ite->second; 378 | } 379 | 380 | for (set::iterator ite = blackSet.begin(); ite != blackSet.end(); ++ite) { 381 | string path = *ite; 382 | if (relativePath.find(path) != string::npos) { 383 | return true; 384 | } 385 | } 386 | } 387 | 388 | return false; 389 | } 390 | 391 | bool SherlockASTVisitor::checkIsNameInBlackList(SourceLocation SL, string name) 392 | { 393 | string blackListPrefix = buildInSetRules[1]; 394 | map>::iterator ite = setRuleMap.find(blackListPrefix); 395 | set blackSet; 396 | if (ite == setRuleMap.end()) { 397 | return false; 398 | } else { 399 | blackSet = ite->second; 400 | } 401 | 402 | for (set::iterator ite = blackSet.begin(); ite != blackSet.end(); ++ite) { 403 | string prefix = *ite; 404 | if (!name.compare(0, prefix.size(), prefix)) { 405 | return true; 406 | } 407 | } 408 | 409 | return false; 410 | } 411 | 412 | bool SherlockASTVisitor::checkIsInSystemHeader(SourceLocation SL) 413 | { 414 | SourceManager &SM = this->context->getSourceManager(); 415 | FullSourceLoc fullLoc = this->context->getFullLoc(SL); 416 | if (SM.isInSystemHeader(fullLoc)) { 417 | return true; 418 | } 419 | 420 | return false; 421 | } 422 | 423 | DiagnosticsEngine::Level SherlockASTVisitor::decideReportType(DiagnosticsEngine::Level level) 424 | { 425 | DiagnosticsEngine::Level result = level; 426 | switch (level) { 427 | case DiagnosticsEngine::Warning: 428 | { 429 | result = this->isForceError() ? DiagnosticsEngine::Error : DiagnosticsEngine::Warning; 430 | } 431 | break; 432 | case DiagnosticsEngine::Error: 433 | { 434 | result = this->isForceWarning() ? DiagnosticsEngine::Warning : DiagnosticsEngine::Error; 435 | } 436 | default: 437 | break; 438 | } 439 | 440 | return result; 441 | } 442 | 443 | bool SherlockASTVisitor::isBuildInBaseRuleEnable(size_t idx) 444 | { 445 | string disabledRule = buildInSetRules[0]; 446 | map>::iterator ite = setRuleMap.find(disabledRule); 447 | set blackSet; 448 | if (ite == setRuleMap.end()) { 449 | return true; 450 | } else { 451 | blackSet = ite->second; 452 | } 453 | 454 | string baseRule = buildInBaseRules[idx]; 455 | 456 | for (set::iterator ite = blackSet.begin(); ite != blackSet.end(); ++ite) { 457 | string blackRule = *ite; 458 | if (!baseRule.compare(blackRule)) { 459 | return false; 460 | } 461 | } 462 | 463 | return true; 464 | } 465 | 466 | bool SherlockASTVisitor::isForceError() 467 | { 468 | string rule = buildInTupleRules[0]; 469 | map::iterator ite = tupleRuleMap.find(rule); 470 | if (ite != tupleRuleMap.end()) { 471 | string forceCast = ite->second; 472 | if (forceCast.find("Error") != string::npos) { 473 | return true; 474 | } 475 | } 476 | 477 | return false; 478 | } 479 | 480 | bool SherlockASTVisitor::isForceWarning() 481 | { 482 | string rule = buildInTupleRules[0]; 483 | map::iterator ite = tupleRuleMap.find(rule); 484 | if (ite != tupleRuleMap.end()) { 485 | string forceCast = ite->second; 486 | if (forceCast.find("Warning") != string::npos) { 487 | return true; 488 | } 489 | } 490 | 491 | return false; 492 | } 493 | 494 | bool SherlockASTVisitor::isSubclassOf(const char *name, ObjCInterfaceDecl *IE) 495 | { 496 | ObjCInterfaceDecl *superIE = IE; 497 | while (superIE != nullptr) { 498 | if (superIE->getName().equals(name)) { 499 | return true; 500 | } 501 | 502 | superIE = superIE->getSuperClass(); 503 | } 504 | return false; 505 | } 506 | 507 | void SherlockASTConsumer::HandleTranslationUnit(ASTContext &context) 508 | { 509 | this->visitor.setContext(context); 510 | this->visitor.TraverseDecl(context.getTranslationUnitDecl()); 511 | } 512 | 513 | unique_ptr SherlockASTAction::CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) 514 | { 515 | return unique_ptr(new SherlockASTConsumer); 516 | } 517 | 518 | bool SherlockASTAction::ParseArgs(const clang::CompilerInstance &CI, const std::vector &args) 519 | { 520 | SherlockParser parser; 521 | tupleRuleMap = parser.parseTupleRule(); 522 | setRuleMap = parser.parseSetRule(); 523 | 524 | return true; 525 | } 526 | } 527 | -------------------------------------------------------------------------------- /Sherlock.exports: -------------------------------------------------------------------------------- 1 | __ZN4llvm8Registry* 2 | -------------------------------------------------------------------------------- /Sherlock.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Sherlock.hpp 3 | // Sherlock 4 | // 5 | // Created by Robbie on 2017/1/29. 6 | // 7 | // 8 | 9 | #ifndef Sherlock_hpp 10 | #define Sherlock_hpp 11 | 12 | #include "clang/AST/AST.h" 13 | #include "clang/AST/RecursiveASTVisitor.h" 14 | #include "clang/AST/ASTConsumer.h" 15 | #include "clang/Frontend/CompilerInstance.h" 16 | #include "clang/FrontEnd/FrontEndPluginRegistry.h" 17 | 18 | #include "SherlockObservePairing.hpp" 19 | #include "SherlockInitDesign.hpp" 20 | 21 | using namespace clang; 22 | using namespace llvm; 23 | using namespace std; 24 | 25 | namespace Sherlock 26 | { 27 | class SherlockASTVisitor : public RecursiveASTVisitor 28 | { 29 | public: 30 | void setContext(ASTContext &context); 31 | bool VisitStmt(Stmt *S); 32 | bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID); 33 | bool VisitObjCImplementationDecl(ObjCImplementationDecl *IMPD); 34 | bool VisitObjCCategoryDecl(ObjCCategoryDecl *CD); 35 | bool VisitObjCPropertyDecl(ObjCPropertyDecl *PD); 36 | bool VisitObjCMethodDecl(ObjCMethodDecl *MD); 37 | private: 38 | ASTContext *context; 39 | 40 | void checkInterfacePrefix(ObjCInterfaceDecl *ID); 41 | void checkInterfaceIllegalCharacters(ObjCInterfaceDecl *ID); 42 | 43 | void checkCopyProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin); 44 | void checkMutableProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin); 45 | void checkDelegateProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin); 46 | void checkAtomicProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin); 47 | void checkUnsafeProperty(ObjCPropertyDecl *PD, string typeStr, ObjCPropertyDecl::PropertyAttributeKind attKin); 48 | 49 | void checkTimerAndDisplayer(ObjCMessageExpr *ME); 50 | 51 | void checkSuperMethod(ObjCMessageExpr *ME); 52 | 53 | void checkObservePairing(ObjCImplementationDecl *IMPD); 54 | 55 | void checkInitDesign(ObjCMethodDecl *MD); 56 | 57 | void checkCategoryMethodNaming(ObjCCategoryDecl *CD); 58 | 59 | bool checkIsSourceInBlackList(SourceLocation SL, string name); 60 | bool checkIsInBlackListPath(SourceLocation SL); 61 | bool checkIsNameInBlackList(SourceLocation SL, string name); 62 | bool checkIsInSystemHeader(SourceLocation SL); 63 | bool isBuildInBaseRuleEnable(size_t idx); 64 | bool isForceError(); 65 | bool isForceWarning(); 66 | DiagnosticsEngine::Level decideReportType(DiagnosticsEngine::Level level); 67 | bool isSubclassOf(const char *name, ObjCInterfaceDecl *ID); 68 | }; 69 | 70 | class SherlockASTConsumer : public ASTConsumer 71 | { 72 | private: 73 | SherlockASTVisitor visitor; 74 | void HandleTranslationUnit(ASTContext &context); 75 | }; 76 | 77 | class SherlockASTAction : public PluginASTAction 78 | { 79 | public: 80 | unique_ptr CreateASTConsumer(CompilerInstance &Compiler,StringRef InFile); 81 | bool ParseArgs(const CompilerInstance &CI, const vector& args); 82 | }; 83 | } 84 | 85 | static clang::FrontendPluginRegistry::Add X("SherlockPlugin", "SherlockPlugin"); 86 | 87 | #endif /* Sherlock_hpp */ 88 | -------------------------------------------------------------------------------- /SherlockConfigure.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SherlockConfigure.hpp 3 | // Sherlock 4 | // 5 | // Created by Robbie on 2017/2/15. 6 | // 7 | // 8 | 9 | #ifndef SherlockConfigure_hpp 10 | #define SherlockConfigure_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | static array buildInTupleRules = {{"force_report", "project_prefix"}}; 17 | 18 | static array buildInSetRules = {{"disabled_rules", "blacklist_prefix", "blacklist_path"}}; 19 | 20 | static array buildInBaseRules = { 21 | {"interface_illegal_character", "project_prefix", "property_atomic", 22 | "property_unsafe", "property_copy", "weak_protocol", 23 | "category_method_naming", "initializer_design", "mutable_property_copy", 24 | "weak_proxy", "super_call", "observe_pairing"}}; 25 | 26 | static string RuleAbsolutePath = "/.../Sherlock/theRule.txt"; 27 | 28 | static string ProjectAbsoluteRootPath = "/.../SherlockTest/"; 29 | 30 | #endif /* SherlockConfigure_hpp */ 31 | -------------------------------------------------------------------------------- /SherlockInitDesign.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SherlockInitDesign.cpp 3 | // Sherlock 4 | // 5 | // Created by Robbie on 2017/2/9. 6 | // 7 | // 8 | 9 | #include "SherlockInitDesign.hpp" 10 | 11 | static int initMethodCount; 12 | 13 | bool SherlockInitDesign::VisitStmt(Stmt *S) 14 | { 15 | const ReturnStmt *RS = dyn_cast_or_null(S); 16 | if (RS) { 17 | this->checkInitDesign(RS); 18 | } 19 | return true; 20 | } 21 | 22 | void SherlockInitDesign::checkInitDesign(const ReturnStmt *RS) 23 | { 24 | if (RS->getRetValue()->isObjCSelfExpr()) { 25 | ++initMethodCount; 26 | } 27 | } 28 | 29 | bool SherlockInitDesign::isDesignatedInitializer() 30 | { 31 | return initMethodCount == 1 || initMethodCount == 0 ? true : false; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /SherlockInitDesign.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SherlockInitDesign.hpp 3 | // Sherlock 4 | // 5 | // Created by Robbie on 2017/2/9. 6 | // 7 | // 8 | 9 | #ifndef SherlockInitDesign_hpp 10 | #define SherlockInitDesign_hpp 11 | 12 | #include 13 | 14 | #include "clang/AST/AST.h" 15 | #include "clang/AST/RecursiveASTVisitor.h" 16 | 17 | using namespace clang; 18 | using namespace llvm; 19 | using namespace std; 20 | 21 | class SherlockInitDesign : public RecursiveASTVisitor 22 | { 23 | public: 24 | bool VisitStmt(Stmt *S); 25 | void checkInitDesign(const ReturnStmt *RS); 26 | bool isDesignatedInitializer(); 27 | }; 28 | 29 | #endif /* SherlockInitDesign_hpp */ 30 | -------------------------------------------------------------------------------- /SherlockObservePairing.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SherlockObservePairing.cpp 3 | // Sherlock 4 | // 5 | // Created by Robbie on 2017/2/6. 6 | // 7 | // 8 | 9 | #include "SherlockObservePairing.hpp" 10 | 11 | static int notRefCount; 12 | static int KVORefCount; 13 | static bool isIgnoreNot; 14 | 15 | bool SherlockObservePairing::VisitStmt(Stmt *S) 16 | { 17 | const ObjCMessageExpr *ME = dyn_cast_or_null(S); 18 | if (ME) { 19 | this->checkNotificationPairing(ME); 20 | this->checkKVOPairing(ME); 21 | } 22 | return true; 23 | } 24 | 25 | void SherlockObservePairing::checkNotificationPairing(const ObjCMessageExpr *ME) 26 | { 27 | ObjCInterfaceDecl *ID = ME->getReceiverInterface(); 28 | if (ID) { 29 | StringRef DName = ID->getName(); 30 | if (DName.equals(StringRef("NSNotificationCenter"))) { 31 | 32 | string selStr = ME->getSelector().getAsString(); 33 | if (selStr.find("addObserver") != string::npos) { 34 | ++notRefCount; 35 | } 36 | 37 | if (selStr.find("removeObserver") != string::npos 38 | && selStr.find("name") != string::npos) { 39 | --notRefCount; 40 | } 41 | 42 | if (selStr.find("removeObserver") != string::npos 43 | && selStr.find("name") == string::npos) { 44 | notRefCount = 0; 45 | isIgnoreNot = true; 46 | } 47 | } 48 | } 49 | } 50 | 51 | void SherlockObservePairing::checkKVOPairing(const ObjCMessageExpr *ME) 52 | { 53 | string selStr = ME->getSelector().getAsString(); 54 | 55 | if (selStr.find("addObserver") != string::npos 56 | && selStr.find("forKeyPath") != string::npos 57 | && selStr.find("options") != string::npos 58 | && selStr.find("context") != string::npos) { 59 | ++KVORefCount; 60 | } 61 | 62 | if (selStr.find("removeObserver") != string::npos 63 | && selStr.find("forKeyPath") != string::npos) { 64 | --KVORefCount; 65 | } 66 | } 67 | 68 | bool SherlockObservePairing::isKVOMissPairing() 69 | { 70 | return KVORefCount == 0 ? false : true; 71 | } 72 | 73 | bool SherlockObservePairing::isNotificationMissPairing() 74 | { 75 | return isIgnoreNot == true ? false : notRefCount == 0 ? false : true; 76 | } 77 | -------------------------------------------------------------------------------- /SherlockObservePairing.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SherlockObservePairing.hpp 3 | // Sherlock 4 | // 5 | // Created by Robbie on 2017/2/6. 6 | // 7 | // 8 | 9 | #ifndef SherlockObservePairing_hpp 10 | #define SherlockObservePairing_hpp 11 | 12 | #include 13 | #include "clang/AST/AST.h" 14 | #include "clang/AST/RecursiveASTVisitor.h" 15 | 16 | using namespace clang; 17 | using namespace llvm; 18 | using namespace std; 19 | 20 | class SherlockObservePairing : public RecursiveASTVisitor 21 | { 22 | public: 23 | bool VisitStmt(Stmt *S); 24 | void checkNotificationPairing(const ObjCMessageExpr *ME); 25 | void checkKVOPairing(const ObjCMessageExpr *ME); 26 | bool isNotificationMissPairing(); 27 | bool isKVOMissPairing(); 28 | }; 29 | 30 | 31 | #endif /* SherlockObservePairing_hpp */ 32 | -------------------------------------------------------------------------------- /SherlockParser.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SherlockParser.cpp 3 | // Sherlock 4 | // 5 | // Created by Robbie on 2017/2/20. 6 | // 7 | // 8 | 9 | #include "SherlockParser.hpp" 10 | 11 | #include 12 | #include 13 | 14 | #include "SherlockConfigure.hpp" 15 | 16 | using namespace std; 17 | 18 | TupleRuleMap SherlockParser::parseTupleRule() 19 | { 20 | TupleRuleMap result; 21 | ifstream file(RuleAbsolutePath); 22 | string line; 23 | if (file.is_open()) { 24 | while (getline(file, line)) { 25 | if (line.find("-") == string::npos 26 | && strcmp(&line.back(), ":")) { 27 | string first; 28 | string second; 29 | string::size_type pos = line.find(":"); 30 | 31 | if (pos != string::npos) { 32 | first = line.substr(0, pos); 33 | second = line.substr(pos + 1 , line.length()); 34 | result.insert(pair(first, second)); 35 | } 36 | } 37 | } 38 | 39 | file.close(); 40 | } 41 | 42 | return result; 43 | } 44 | 45 | SetRuleMap SherlockParser::parseSetRule() 46 | { 47 | SetRuleMap result; 48 | ifstream file(RuleAbsolutePath); 49 | string line; 50 | if (file.is_open()) { 51 | while (getline(file, line)) { 52 | for (auto ite = begin(buildInSetRules); ite != end(buildInSetRules); ++ite) { 53 | string rule = *ite; 54 | if (line.find(rule) != string::npos) { 55 | 56 | set set; 57 | 58 | while (getline(file, line)) { 59 | string element; 60 | string::size_type pos = line.find("-"); 61 | 62 | if (pos != string::npos) { 63 | element = line.substr(pos + 2, line.length()); 64 | set.insert(element); 65 | } else if (line.find("~end") != string::npos) { 66 | break; 67 | } 68 | } 69 | 70 | if (!set.empty()) { 71 | result.insert(pair>(rule, set)); 72 | } 73 | } 74 | } 75 | } 76 | 77 | file.close(); 78 | } 79 | 80 | return result; 81 | } 82 | 83 | -------------------------------------------------------------------------------- /SherlockParser.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SherlockParser.hpp 3 | // Sherlock 4 | // 5 | // Created by Robbie on 2017/2/20. 6 | // 7 | // 8 | 9 | #ifndef SherlockParser_hpp 10 | #define SherlockParser_hpp 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace std; 19 | 20 | typedef map TupleRuleMap; 21 | static TupleRuleMap tupleRuleMap; 22 | 23 | typedef map> SetRuleMap; 24 | static SetRuleMap setRuleMap; 25 | 26 | class SherlockParser { 27 | public: 28 | TupleRuleMap parseTupleRule(); 29 | SetRuleMap parseSetRule(); 30 | }; 31 | 32 | #endif /* SherlockParser_hpp */ 33 | -------------------------------------------------------------------------------- /SherlockTest/AFViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AFViewController.h 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/14. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AFViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SherlockTest/AFViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AFViewController.m 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/14. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import "AFViewController.h" 10 | 11 | @protocol AFSherlockDelegate 12 | 13 | @end 14 | 15 | typedef void(^AFSherlockBlock)(void); 16 | 17 | 18 | @interface AFViewController () 19 | 20 | @property (nonatomic, strong) NSTimer *timer; 21 | 22 | @property (nonatomic, strong) id delegate; 23 | 24 | @property (nonatomic, strong) AFSherlockBlock block; 25 | 26 | @property (nonatomic, copy) NSMutableArray *mutableArr; 27 | 28 | @property (nonatomic, strong) NSString *str; 29 | 30 | @property (nonatomic, unsafe_unretained) NSNumber *num; 31 | 32 | @property (atomic, strong) NSDictionary *dic; 33 | 34 | @end 35 | 36 | @implementation AFViewController 37 | 38 | - (instancetype)initWithDic:(NSDictionary *)dic { 39 | if (self) {} 40 | return self; 41 | } 42 | 43 | - (instancetype)initWithArr:(NSArray *)arr { 44 | if (self) {} 45 | return self; 46 | } 47 | 48 | - (void)viewDidLoad { 49 | [super viewDidLoad]; 50 | 51 | self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(placeHolderMethod) userInfo:nil repeats:YES]; 52 | 53 | if ([super respondsToSelector:@selector(viewDidLoad)]) { 54 | //answer is YES 55 | } 56 | 57 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(placeHolderMethod) name:@"ErrorNotification" object:nil]; 58 | 59 | [self.view addObserver:self forKeyPath:@"alpha" options:NSKeyValueObservingOptionNew context:nil]; 60 | [self.view addObserver:self forKeyPath:@"opaque" options:NSKeyValueObservingOptionNew context:nil]; 61 | } 62 | 63 | - (void)dealloc { 64 | [self.view removeObserver:self forKeyPath:@"alpha"]; 65 | } 66 | 67 | - (void)placeHolderMethod{} 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /SherlockTest/Even_Wrong_More_ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Even_Wrong_More_ViewController.h 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/23. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Even_Wrong_More_ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SherlockTest/Even_Wrong_More_ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Even_Wrong_More_ViewController.m 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/23. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import "Even_Wrong_More_ViewController.h" 10 | 11 | @interface Even_Wrong_More_ViewController () 12 | 13 | @end 14 | 15 | @implementation Even_Wrong_More_ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view. 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | /* 28 | #pragma mark - Navigation 29 | 30 | // In a storyboard-based application, you will often want to do a little preparation before navigation 31 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 32 | // Get the new view controller using [segue destinationViewController]. 33 | // Pass the selected object to the new view controller. 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /SherlockTest/NSString+AFSherlock.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+AFSherlock.h 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/15. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (AFSherlock) 12 | 13 | - (NSString *)afSherlockSearch; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SherlockTest/NSString+AFSherlock.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+AFSherlock.m 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/15. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import "NSString+AFSherlock.h" 10 | 11 | @implementation NSString (AFSherlock) 12 | 13 | - (NSString *)afSherlockSearch { 14 | return self; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SherlockTest/NSString+Sherlock.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Sherlock.h 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/14. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (Sherlock) 12 | 13 | - (NSString *)sherlockSearch; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SherlockTest/NSString+Sherlock.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Sherlock.m 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/14. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import "NSString+Sherlock.h" 10 | 11 | @implementation NSString (Sherlock) 12 | 13 | - (NSString *)sherlockSearch { 14 | return self; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SherlockTest/SherlockViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SherlockViewController.h 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/10. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SherlockViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SherlockTest/SherlockViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SherlockViewController.m 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/10. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import "SherlockViewController.h" 10 | 11 | #import 12 | 13 | @protocol SherlockDelegate 14 | 15 | @end 16 | 17 | typedef void(^SherlockBlock)(void); 18 | 19 | @interface SherlockViewController () 20 | 21 | @property (nonatomic, strong) NSTimer *timer; 22 | 23 | @property (nonatomic, strong) id delegate; 24 | 25 | @property (nonatomic, strong) SherlockBlock block; 26 | 27 | @property (nonatomic, copy) NSMutableArray *mutableArr; 28 | 29 | @property (nonatomic, strong) NSString *str; 30 | 31 | @property (nonatomic, unsafe_unretained) NSNumber *num; 32 | 33 | @property (atomic, strong) NSDictionary *dic; 34 | 35 | @end 36 | 37 | @implementation SherlockViewController 38 | 39 | - (instancetype)initWithDic:(NSDictionary *)dic { 40 | if (self) {} 41 | return self; 42 | } 43 | 44 | - (instancetype)initWithArr:(NSArray *)arr { 45 | if (self) {} 46 | return self; 47 | } 48 | 49 | - (void)viewDidLoad { 50 | [super viewDidLoad]; 51 | 52 | self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(placeHolderMethod) userInfo:nil repeats:YES]; 53 | 54 | if ([super respondsToSelector:@selector(viewDidLoad)]) { 55 | //answer is YES 56 | } 57 | 58 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(placeHolderMethod) name:@"ErrorNotification" object:nil]; 59 | 60 | [self.view addObserver:self forKeyPath:@"alpha" options:NSKeyValueObservingOptionNew context:nil]; 61 | [self.view addObserver:self forKeyPath:@"opaque" options:NSKeyValueObservingOptionNew context:nil]; 62 | } 63 | 64 | - (void)dealloc { 65 | [self.view removeObserver:self forKeyPath:@"alpha"]; 66 | } 67 | 68 | - (void)placeHolderMethod{} 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /SherlockTest/ThirdPart/ThirdPartViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ThirdPartViewController.h 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/22. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ThirdPartViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SherlockTest/ThirdPart/ThirdPartViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ThirdPartViewController.m 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/22. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import "ThirdPartViewController.h" 10 | 11 | @protocol ThirdPartDelegate 12 | 13 | @end 14 | 15 | typedef void(^ThirdPartBlock)(void); 16 | 17 | 18 | @interface ThirdPartViewController () 19 | 20 | @property (nonatomic, strong) NSTimer *timer; 21 | 22 | @property (nonatomic, strong) id delegate; 23 | 24 | @property (nonatomic, strong) ThirdPartBlock block; 25 | 26 | @property (nonatomic, copy) NSMutableArray *mutableArr; 27 | 28 | @property (nonatomic, strong) NSString *str; 29 | 30 | @property (nonatomic, unsafe_unretained) NSNumber *num; 31 | 32 | @property (atomic, strong) NSDictionary *dic; 33 | 34 | @end 35 | 36 | @implementation ThirdPartViewController 37 | 38 | - (instancetype)initWithDic:(NSDictionary *)dic { 39 | if (self) {} 40 | return self; 41 | } 42 | 43 | - (instancetype)initWithArr:(NSArray *)arr { 44 | if (self) {} 45 | return self; 46 | } 47 | 48 | - (void)viewDidLoad { 49 | [super viewDidLoad]; 50 | 51 | self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(placeHolderMethod) userInfo:nil repeats:YES]; 52 | 53 | if ([super respondsToSelector:@selector(viewDidLoad)]) { 54 | //answer is YES 55 | } 56 | 57 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(placeHolderMethod) name:@"ErrorNotification" object:nil]; 58 | 59 | [self.view addObserver:self forKeyPath:@"alpha" options:NSKeyValueObservingOptionNew context:nil]; 60 | [self.view addObserver:self forKeyPath:@"opaque" options:NSKeyValueObservingOptionNew context:nil]; 61 | } 62 | 63 | - (void)dealloc { 64 | [self.view removeObserver:self forKeyPath:@"alpha"]; 65 | } 66 | 67 | - (void)placeHolderMethod{} 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /SherlockTest/WrongNameViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WrongNameViewController.h 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/22. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WrongNameViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SherlockTest/WrongNameViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WrongNameViewController.m 3 | // SherlockTest 4 | // 5 | // Created by Robbie on 2017/2/22. 6 | // Copyright © 2017年 Robbie. All rights reserved. 7 | // 8 | 9 | #import "WrongNameViewController.h" 10 | 11 | @interface WrongNameViewController () 12 | 13 | @end 14 | 15 | @implementation WrongNameViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view. 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | /* 28 | #pragma mark - Navigation 29 | 30 | // In a storyboard-based application, you will often want to do a little preparation before navigation 31 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 32 | // Get the new view controller using [segue destinationViewController]. 33 | // Pass the selected object to the new view controller. 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /theRule.txt: -------------------------------------------------------------------------------- 1 | 2 | disabled_rules: 3 | ~end 4 | 5 | force_report: Warning 6 | 7 | project_prefix: Sherlock 8 | 9 | blacklist_path: 10 | - Pods 11 | - ThirdPart 12 | ~end 13 | 14 | blacklist_prefix: 15 | - AF 16 | - SD 17 | - FM 18 | - YY 19 | ~end --------------------------------------------------------------------------------