xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
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