1311118Sdim//==- ObjCPropertyChecker.cpp - Check ObjC properties ------------*- C++ -*-==// 2311118Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6311118Sdim// 7311118Sdim//===----------------------------------------------------------------------===// 8311118Sdim// 9311118Sdim// This checker finds issues with Objective-C properties. 10311118Sdim// Currently finds only one kind of issue: 11311118Sdim// - Find synthesized properties with copy attribute of mutable NS collection 12311118Sdim// types. Calling -copy on such collections produces an immutable copy, 13311118Sdim// which contradicts the type of the property. 14311118Sdim// 15311118Sdim//===----------------------------------------------------------------------===// 16311118Sdim 17344779Sdim#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18311118Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19311118Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 20311118Sdim 21311118Sdimusing namespace clang; 22311118Sdimusing namespace ento; 23311118Sdim 24311118Sdimnamespace { 25311118Sdimclass ObjCPropertyChecker 26311118Sdim : public Checker<check::ASTDecl<ObjCPropertyDecl>> { 27311118Sdim void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const; 28311118Sdim 29311118Sdimpublic: 30311118Sdim void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr, 31311118Sdim BugReporter &BR) const; 32311118Sdim}; 33311118Sdim} // end anonymous namespace. 34311118Sdim 35311118Sdimvoid ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D, 36311118Sdim AnalysisManager &Mgr, 37311118Sdim BugReporter &BR) const { 38311118Sdim checkCopyMutable(D, BR); 39311118Sdim} 40311118Sdim 41311118Sdimvoid ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D, 42311118Sdim BugReporter &BR) const { 43311118Sdim if (D->isReadOnly() || D->getSetterKind() != ObjCPropertyDecl::Copy) 44311118Sdim return; 45311118Sdim 46311118Sdim QualType T = D->getType(); 47311118Sdim if (!T->isObjCObjectPointerType()) 48311118Sdim return; 49311118Sdim 50311118Sdim const std::string &PropTypeName(T->getPointeeType().getCanonicalType() 51311118Sdim .getUnqualifiedType() 52311118Sdim .getAsString()); 53311118Sdim if (!StringRef(PropTypeName).startswith("NSMutable")) 54311118Sdim return; 55311118Sdim 56311118Sdim const ObjCImplDecl *ImplD = nullptr; 57311118Sdim if (const ObjCInterfaceDecl *IntD = 58311118Sdim dyn_cast<ObjCInterfaceDecl>(D->getDeclContext())) { 59311118Sdim ImplD = IntD->getImplementation(); 60321369Sdim } else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) { 61311118Sdim ImplD = CatD->getClassInterface()->getImplementation(); 62311118Sdim } 63311118Sdim 64311118Sdim if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D)) 65311118Sdim return; 66311118Sdim 67311118Sdim SmallString<128> Str; 68311118Sdim llvm::raw_svector_ostream OS(Str); 69311118Sdim OS << "Property of mutable type '" << PropTypeName 70311118Sdim << "' has 'copy' attribute; an immutable object will be stored instead"; 71311118Sdim 72311118Sdim BR.EmitBasicReport( 73311118Sdim D, this, "Objective-C property misuse", "Logic error", OS.str(), 74311118Sdim PathDiagnosticLocation::createBegin(D, BR.getSourceManager()), 75311118Sdim D->getSourceRange()); 76311118Sdim} 77311118Sdim 78311118Sdimvoid ento::registerObjCPropertyChecker(CheckerManager &Mgr) { 79311118Sdim Mgr.registerChecker<ObjCPropertyChecker>(); 80311118Sdim} 81353358Sdim 82353358Sdimbool ento::shouldRegisterObjCPropertyChecker(const LangOptions &LO) { 83353358Sdim return true; 84353358Sdim} 85