blob: b9857e51f3eaac4494b7c5cd6c5e5d3917cba407 [file] [log] [blame]
Artem Dergacheve69d2e42016-12-13 17:19:18 +00001//==- 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 "ClangSACheckers.h"
19#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20#include "clang/StaticAnalyzer/Core/Checker.h"
21
22using namespace clang;
23using namespace ento;
24
25namespace {
26class ObjCPropertyChecker
27 : public Checker<check::ASTDecl<ObjCPropertyDecl>> {
28 void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const;
29
30public:
31 void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr,
32 BugReporter &BR) const;
33};
34} // end anonymous namespace.
35
36void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D,
37 AnalysisManager &Mgr,
38 BugReporter &BR) const {
39 checkCopyMutable(D, BR);
40}
41
42void 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 {
62 const ObjCCategoryDecl *CatD = cast<ObjCCategoryDecl>(D->getDeclContext());
63 ImplD = CatD->getClassInterface()->getImplementation();
64 }
65
66 if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D))
67 return;
68
69 SmallString<128> Str;
70 llvm::raw_svector_ostream OS(Str);
71 OS << "Property of mutable type '" << PropTypeName
72 << "' has 'copy' attribute; an immutable object will be stored instead";
73
74 BR.EmitBasicReport(
75 D, this, "Objective-C property misuse", "Logic error", OS.str(),
76 PathDiagnosticLocation::createBegin(D, BR.getSourceManager()),
77 D->getSourceRange());
78}
79
80void ento::registerObjCPropertyChecker(CheckerManager &Mgr) {
81 Mgr.registerChecker<ObjCPropertyChecker>();
82}