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 21using namespace clang; 22using namespace ento; 23 24namespace { 25class ObjCPropertyChecker 26 : public Checker<check::ASTDecl<ObjCPropertyDecl>> { 27 void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const; 28 29public: 30 void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr, 31 BugReporter &BR) const; 32}; 33} // end anonymous namespace. 34 35void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D, 36 AnalysisManager &Mgr, 37 BugReporter &BR) const { 38 checkCopyMutable(D, BR); 39} 40 41void 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).startswith("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 78void ento::registerObjCPropertyChecker(CheckerManager &Mgr) { 79 Mgr.registerChecker<ObjCPropertyChecker>(); 80} 81 82bool ento::shouldRegisterObjCPropertyChecker(const CheckerManager &mgr) { 83 return true; 84} 85