blob: dfd2c9afe7fb055cb4ec4b563cd1a38c055cc06f [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();
Devin Coughlin1bf65c82017-03-01 01:47:37 +000061 } else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) {
Artem Dergacheve69d2e42016-12-13 17:19:18 +000062 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
79void ento::registerObjCPropertyChecker(CheckerManager &Mgr) {
80 Mgr.registerChecker<ObjCPropertyChecker>();
81}