1 //==- ObjCPropertyChecker.cpp - Check ObjC properties ------------*- C++ -*-==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This checker finds issues with Objective-C properties. 10 // Currently finds only one kind of issue: 11 // - Find synthesized properties with copy attribute of mutable NS collection 12 // types. Calling -copy on such collections produces an immutable copy, 13 // which contradicts the type of the property. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19 #include "clang/StaticAnalyzer/Core/Checker.h" 20 21 using namespace clang; 22 using namespace ento; 23 24 namespace { 25 class ObjCPropertyChecker 26 : public Checker<check::ASTDecl<ObjCPropertyDecl>> { 27 void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const; 28 29 public: 30 void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr, 31 BugReporter &BR) const; 32 }; 33 } // end anonymous namespace. 34 35 void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D, 36 AnalysisManager &Mgr, 37 BugReporter &BR) const { 38 checkCopyMutable(D, BR); 39 } 40 41 void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D, 42 BugReporter &BR) const { 43 if (D->isReadOnly() || D->getSetterKind() != ObjCPropertyDecl::Copy) 44 return; 45 46 QualType T = D->getType(); 47 if (!T->isObjCObjectPointerType()) 48 return; 49 50 const std::string &PropTypeName(T->getPointeeType().getCanonicalType() 51 .getUnqualifiedType() 52 .getAsString()); 53 if (!StringRef(PropTypeName).starts_with("NSMutable")) 54 return; 55 56 const ObjCImplDecl *ImplD = nullptr; 57 if (const ObjCInterfaceDecl *IntD = 58 dyn_cast<ObjCInterfaceDecl>(D->getDeclContext())) { 59 ImplD = IntD->getImplementation(); 60 } else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) { 61 ImplD = CatD->getClassInterface()->getImplementation(); 62 } 63 64 if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D)) 65 return; 66 67 SmallString<128> Str; 68 llvm::raw_svector_ostream OS(Str); 69 OS << "Property of mutable type '" << PropTypeName 70 << "' has 'copy' attribute; an immutable object will be stored instead"; 71 72 BR.EmitBasicReport( 73 D, this, "Objective-C property misuse", "Logic error", OS.str(), 74 PathDiagnosticLocation::createBegin(D, BR.getSourceManager()), 75 D->getSourceRange()); 76 } 77 78 void ento::registerObjCPropertyChecker(CheckerManager &Mgr) { 79 Mgr.registerChecker<ObjCPropertyChecker>(); 80 } 81 82 bool ento::shouldRegisterObjCPropertyChecker(const CheckerManager &mgr) { 83 return true; 84 } 85