1 //==- ObjCPropertyChecker.cpp - Check ObjC properties ------------*- C++ -*-==//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This checker finds issues with Objective-C properties.
11 // Currently finds only one kind of issue:
12 // - Find synthesized properties with copy attribute of mutable NS collection
13 // types. Calling -copy on such collections produces an immutable copy,
14 // which contradicts the type of the property.
15 //
16 //===----------------------------------------------------------------------===//
17
18 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20 #include "clang/StaticAnalyzer/Core/Checker.h"
21
22 using namespace clang;
23 using namespace ento;
24
25 namespace {
26 class ObjCPropertyChecker
27 : public Checker<check::ASTDecl<ObjCPropertyDecl>> {
28 void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const;
29
30 public:
31 void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr,
32 BugReporter &BR) const;
33 };
34 } // end anonymous namespace.
35
checkASTDecl(const ObjCPropertyDecl * D,AnalysisManager & Mgr,BugReporter & BR) const36 void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D,
37 AnalysisManager &Mgr,
38 BugReporter &BR) const {
39 checkCopyMutable(D, BR);
40 }
41
checkCopyMutable(const ObjCPropertyDecl * D,BugReporter & BR) const42 void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D,
43 BugReporter &BR) const {
44 if (D->isReadOnly() || D->getSetterKind() != ObjCPropertyDecl::Copy)
45 return;
46
47 QualType T = D->getType();
48 if (!T->isObjCObjectPointerType())
49 return;
50
51 const std::string &PropTypeName(T->getPointeeType().getCanonicalType()
52 .getUnqualifiedType()
53 .getAsString());
54 if (!StringRef(PropTypeName).startswith("NSMutable"))
55 return;
56
57 const ObjCImplDecl *ImplD = nullptr;
58 if (const ObjCInterfaceDecl *IntD =
59 dyn_cast<ObjCInterfaceDecl>(D->getDeclContext())) {
60 ImplD = IntD->getImplementation();
61 } else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) {
62 ImplD = CatD->getClassInterface()->getImplementation();
63 }
64
65 if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D))
66 return;
67
68 SmallString<128> Str;
69 llvm::raw_svector_ostream OS(Str);
70 OS << "Property of mutable type '" << PropTypeName
71 << "' has 'copy' attribute; an immutable object will be stored instead";
72
73 BR.EmitBasicReport(
74 D, this, "Objective-C property misuse", "Logic error", OS.str(),
75 PathDiagnosticLocation::createBegin(D, BR.getSourceManager()),
76 D->getSourceRange());
77 }
78
registerObjCPropertyChecker(CheckerManager & Mgr)79 void ento::registerObjCPropertyChecker(CheckerManager &Mgr) {
80 Mgr.registerChecker<ObjCPropertyChecker>();
81 }
82