17330f729Sjoerg //==- ObjCPropertyChecker.cpp - Check ObjC properties ------------*- C++ -*-==//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This checker finds issues with Objective-C properties.
107330f729Sjoerg // Currently finds only one kind of issue:
117330f729Sjoerg // - Find synthesized properties with copy attribute of mutable NS collection
127330f729Sjoerg // types. Calling -copy on such collections produces an immutable copy,
137330f729Sjoerg // which contradicts the type of the property.
147330f729Sjoerg //
157330f729Sjoerg //===----------------------------------------------------------------------===//
167330f729Sjoerg
177330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
197330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
207330f729Sjoerg
217330f729Sjoerg using namespace clang;
227330f729Sjoerg using namespace ento;
237330f729Sjoerg
247330f729Sjoerg namespace {
257330f729Sjoerg class ObjCPropertyChecker
267330f729Sjoerg : public Checker<check::ASTDecl<ObjCPropertyDecl>> {
277330f729Sjoerg void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const;
287330f729Sjoerg
297330f729Sjoerg public:
307330f729Sjoerg void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr,
317330f729Sjoerg BugReporter &BR) const;
327330f729Sjoerg };
337330f729Sjoerg } // end anonymous namespace.
347330f729Sjoerg
checkASTDecl(const ObjCPropertyDecl * D,AnalysisManager & Mgr,BugReporter & BR) const357330f729Sjoerg void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D,
367330f729Sjoerg AnalysisManager &Mgr,
377330f729Sjoerg BugReporter &BR) const {
387330f729Sjoerg checkCopyMutable(D, BR);
397330f729Sjoerg }
407330f729Sjoerg
checkCopyMutable(const ObjCPropertyDecl * D,BugReporter & BR) const417330f729Sjoerg void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D,
427330f729Sjoerg BugReporter &BR) const {
437330f729Sjoerg if (D->isReadOnly() || D->getSetterKind() != ObjCPropertyDecl::Copy)
447330f729Sjoerg return;
457330f729Sjoerg
467330f729Sjoerg QualType T = D->getType();
477330f729Sjoerg if (!T->isObjCObjectPointerType())
487330f729Sjoerg return;
497330f729Sjoerg
507330f729Sjoerg const std::string &PropTypeName(T->getPointeeType().getCanonicalType()
517330f729Sjoerg .getUnqualifiedType()
527330f729Sjoerg .getAsString());
537330f729Sjoerg if (!StringRef(PropTypeName).startswith("NSMutable"))
547330f729Sjoerg return;
557330f729Sjoerg
567330f729Sjoerg const ObjCImplDecl *ImplD = nullptr;
577330f729Sjoerg if (const ObjCInterfaceDecl *IntD =
587330f729Sjoerg dyn_cast<ObjCInterfaceDecl>(D->getDeclContext())) {
597330f729Sjoerg ImplD = IntD->getImplementation();
607330f729Sjoerg } else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) {
617330f729Sjoerg ImplD = CatD->getClassInterface()->getImplementation();
627330f729Sjoerg }
637330f729Sjoerg
647330f729Sjoerg if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D))
657330f729Sjoerg return;
667330f729Sjoerg
677330f729Sjoerg SmallString<128> Str;
687330f729Sjoerg llvm::raw_svector_ostream OS(Str);
697330f729Sjoerg OS << "Property of mutable type '" << PropTypeName
707330f729Sjoerg << "' has 'copy' attribute; an immutable object will be stored instead";
717330f729Sjoerg
727330f729Sjoerg BR.EmitBasicReport(
737330f729Sjoerg D, this, "Objective-C property misuse", "Logic error", OS.str(),
747330f729Sjoerg PathDiagnosticLocation::createBegin(D, BR.getSourceManager()),
757330f729Sjoerg D->getSourceRange());
767330f729Sjoerg }
777330f729Sjoerg
registerObjCPropertyChecker(CheckerManager & Mgr)787330f729Sjoerg void ento::registerObjCPropertyChecker(CheckerManager &Mgr) {
797330f729Sjoerg Mgr.registerChecker<ObjCPropertyChecker>();
807330f729Sjoerg }
817330f729Sjoerg
shouldRegisterObjCPropertyChecker(const CheckerManager & mgr)82*e038c9c4Sjoerg bool ento::shouldRegisterObjCPropertyChecker(const CheckerManager &mgr) {
837330f729Sjoerg return true;
847330f729Sjoerg }
85