//==- ObjCPropertyChecker.cpp - Check ObjC properties ------------*- C++ -*-==// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This checker finds issues with Objective-C properties. // Currently finds only one kind of issue: // - Find synthesized properties with copy attribute of mutable NS collection // types. Calling -copy on such collections produces an immutable copy, // which contradicts the type of the property. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" using namespace clang; using namespace ento; namespace { class ObjCPropertyChecker : public Checker> { void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const; public: void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr, BugReporter &BR) const; }; } // end anonymous namespace. void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr, BugReporter &BR) const { checkCopyMutable(D, BR); } void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const { if (D->isReadOnly() || D->getSetterKind() != ObjCPropertyDecl::Copy) return; QualType T = D->getType(); if (!T->isObjCObjectPointerType()) return; const std::string &PropTypeName(T->getPointeeType().getCanonicalType() .getUnqualifiedType() .getAsString()); if (!StringRef(PropTypeName).startswith("NSMutable")) return; const ObjCImplDecl *ImplD = nullptr; if (const ObjCInterfaceDecl *IntD = dyn_cast(D->getDeclContext())) { ImplD = IntD->getImplementation(); } else if (auto *CatD = dyn_cast(D->getDeclContext())) { ImplD = CatD->getClassInterface()->getImplementation(); } if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D)) return; SmallString<128> Str; llvm::raw_svector_ostream OS(Str); OS << "Property of mutable type '" << PropTypeName << "' has 'copy' attribute; an immutable object will be stored instead"; BR.EmitBasicReport( D, this, "Objective-C property misuse", "Logic error", OS.str(), PathDiagnosticLocation::createBegin(D, BR.getSourceManager()), D->getSourceRange()); } void ento::registerObjCPropertyChecker(CheckerManager &Mgr) { Mgr.registerChecker(); } bool ento::shouldRegisterObjCPropertyChecker(const LangOptions &LO) { return true; }