blob: 0058f3d3881fca449dd2748bac66d49e60a9dd09 [file] [log] [blame]
Anna Zaks461f2392012-09-27 19:45:15 +00001//=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Anna Zaks461f2392012-09-27 19:45:15 +00006//
7//===----------------------------------------------------------------------===//
8//
Anna Zaks0e9c9412013-01-17 23:24:58 +00009// Check that Objective C properties are set with the setter, not though a
10// direct assignment.
11//
12// Two versions of a checker exist: one that checks all methods and the other
13// that only checks the methods annotated with
14// __attribute__((annotate("objc_no_direct_instance_variable_assignment")))
15//
16// The checker does not warn about assignments to Ivars, annotated with
17// __attribute__((objc_allow_direct_instance_variable_assignment"))). This
18// annotation serves as a false positive suppression mechanism for the
19// checker. The annotation is allowed on properties and Ivars.
Anna Zaks461f2392012-09-27 19:45:15 +000020//
21//===----------------------------------------------------------------------===//
22
Kristof Umann76a21502018-12-15 16:23:51 +000023#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
Anna Zaks25dd07c2012-12-05 01:14:37 +000024#include "clang/AST/Attr.h"
Anna Zaks461f2392012-09-27 19:45:15 +000025#include "clang/AST/DeclObjC.h"
26#include "clang/AST/StmtVisitor.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000027#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
28#include "clang/StaticAnalyzer/Core/Checker.h"
29#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
Anna Zaks461f2392012-09-27 19:45:15 +000030#include "llvm/ADT/DenseMap.h"
31
32using namespace clang;
33using namespace ento;
34
35namespace {
36
Anna Zaks25dd07c2012-12-05 01:14:37 +000037/// The default method filter, which is used to filter out the methods on which
38/// the check should not be performed.
39///
40/// Checks for the init, dealloc, and any other functions that might be allowed
41/// to perform direct instance variable assignment based on their name.
Benjamin Kramer27bc5042013-08-09 17:17:42 +000042static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
Alexander Kornienko9c104902015-12-28 13:06:58 +000043 return M->getMethodFamily() == OMF_init ||
44 M->getMethodFamily() == OMF_dealloc ||
45 M->getMethodFamily() == OMF_copy ||
46 M->getMethodFamily() == OMF_mutableCopy ||
47 M->getSelector().getNameForSlot(0).find("init") != StringRef::npos ||
48 M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos;
Benjamin Kramer27bc5042013-08-09 17:17:42 +000049}
Anna Zaks25dd07c2012-12-05 01:14:37 +000050
Anna Zaks461f2392012-09-27 19:45:15 +000051class DirectIvarAssignment :
52 public Checker<check::ASTDecl<ObjCImplementationDecl> > {
53
54 typedef llvm::DenseMap<const ObjCIvarDecl*,
55 const ObjCPropertyDecl*> IvarToPropertyMapTy;
56
57 /// A helper class, which walks the AST and locates all assignments to ivars
58 /// in the given function.
59 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
60 const IvarToPropertyMapTy &IvarToPropMap;
61 const ObjCMethodDecl *MD;
62 const ObjCInterfaceDecl *InterfD;
63 BugReporter &BR;
Alexander Kornienko4aca9b12014-02-11 21:49:21 +000064 const CheckerBase *Checker;
Anna Zaks461f2392012-09-27 19:45:15 +000065 LocationOrAnalysisDeclContext DCtx;
66
67 public:
68 MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
Alexander Kornienko4aca9b12014-02-11 21:49:21 +000069 const ObjCInterfaceDecl *InID, BugReporter &InBR,
70 const CheckerBase *Checker, AnalysisDeclContext *InDCtx)
71 : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR),
72 Checker(Checker), DCtx(InDCtx) {}
Anna Zaks461f2392012-09-27 19:45:15 +000073
74 void VisitStmt(const Stmt *S) { VisitChildren(S); }
75
76 void VisitBinaryOperator(const BinaryOperator *BO);
77
78 void VisitChildren(const Stmt *S) {
Benjamin Kramer642f1732015-07-02 21:03:14 +000079 for (const Stmt *Child : S->children())
80 if (Child)
81 this->Visit(Child);
Anna Zaks461f2392012-09-27 19:45:15 +000082 }
83 };
84
85public:
Benjamin Kramer27bc5042013-08-09 17:17:42 +000086 bool (*ShouldSkipMethod)(const ObjCMethodDecl *);
Anna Zaks25dd07c2012-12-05 01:14:37 +000087
88 DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {}
89
Anna Zaks461f2392012-09-27 19:45:15 +000090 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
91 BugReporter &BR) const;
92};
93
94static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD,
Anna Zaks6aef4552012-09-27 21:57:17 +000095 const ObjCInterfaceDecl *InterD,
96 ASTContext &Ctx) {
Anna Zaks461f2392012-09-27 19:45:15 +000097 // Check for synthesized ivars.
98 ObjCIvarDecl *ID = PD->getPropertyIvarDecl();
99 if (ID)
100 return ID;
101
Anna Zaks6aef4552012-09-27 21:57:17 +0000102 ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD);
103
Anna Zaks461f2392012-09-27 19:45:15 +0000104 // Check for existing "_PropName".
Anna Zaks6aef4552012-09-27 21:57:17 +0000105 ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx));
Anna Zaks461f2392012-09-27 19:45:15 +0000106 if (ID)
107 return ID;
108
109 // Check for existing "PropName".
110 IdentifierInfo *PropIdent = PD->getIdentifier();
Anna Zaks6aef4552012-09-27 21:57:17 +0000111 ID = NonConstInterD->lookupInstanceVariable(PropIdent);
Anna Zaks461f2392012-09-27 19:45:15 +0000112
113 return ID;
114}
115
116void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,
117 AnalysisManager& Mgr,
118 BugReporter &BR) const {
119 const ObjCInterfaceDecl *InterD = D->getClassInterface();
120
121
122 IvarToPropertyMapTy IvarToPropMap;
123
124 // Find all properties for this class.
Manman Rena7a8b1f2016-01-26 18:05:23 +0000125 for (const auto *PD : InterD->instance_properties()) {
Anna Zaks461f2392012-09-27 19:45:15 +0000126 // Find the corresponding IVar.
Anna Zaks6aef4552012-09-27 21:57:17 +0000127 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,
128 Mgr.getASTContext());
Anna Zaks461f2392012-09-27 19:45:15 +0000129
130 if (!ID)
131 continue;
132
133 // Store the IVar to property mapping.
134 IvarToPropMap[ID] = PD;
135 }
136
137 if (IvarToPropMap.empty())
138 return;
139
Aaron Ballmanf26acce2014-03-13 19:50:17 +0000140 for (const auto *M : D->instance_methods()) {
Anna Zaks461f2392012-09-27 19:45:15 +0000141 AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
142
Anna Zaks25dd07c2012-12-05 01:14:37 +0000143 if ((*ShouldSkipMethod)(M))
Anna Zaks461f2392012-09-27 19:45:15 +0000144 continue;
145
146 const Stmt *Body = M->getBody();
Anna Zaks6aef4552012-09-27 21:57:17 +0000147 assert(Body);
Anna Zaks461f2392012-09-27 19:45:15 +0000148
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000149 MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
150 DCtx);
Anna Zaks461f2392012-09-27 19:45:15 +0000151 MC.VisitStmt(Body);
152 }
153}
154
Anna Zaks0e9c9412013-01-17 23:24:58 +0000155static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {
Aaron Ballmanbe22bcb2014-03-10 17:08:28 +0000156 for (const auto *Ann : D->specific_attrs<AnnotateAttr>())
Anna Zaks65195642013-01-16 01:36:00 +0000157 if (Ann->getAnnotation() ==
158 "objc_allow_direct_instance_variable_assignment")
159 return true;
Anna Zaks65195642013-01-16 01:36:00 +0000160 return false;
161}
162
Anna Zaks461f2392012-09-27 19:45:15 +0000163void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
164 const BinaryOperator *BO) {
165 if (!BO->isAssignmentOp())
166 return;
167
Anna Zaks6aef4552012-09-27 21:57:17 +0000168 const ObjCIvarRefExpr *IvarRef =
169 dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());
Anna Zaks461f2392012-09-27 19:45:15 +0000170
171 if (!IvarRef)
172 return;
173
174 if (const ObjCIvarDecl *D = IvarRef->getDecl()) {
175 IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);
Anna Zaks65195642013-01-16 01:36:00 +0000176
Anna Zaks461f2392012-09-27 19:45:15 +0000177 if (I != IvarToPropMap.end()) {
178 const ObjCPropertyDecl *PD = I->second;
Anna Zaks0e9c9412013-01-17 23:24:58 +0000179 // Skip warnings on Ivars, annotated with
Anna Zaks65195642013-01-16 01:36:00 +0000180 // objc_allow_direct_instance_variable_assignment. This annotation serves
Anna Zaks0e9c9412013-01-17 23:24:58 +0000181 // as a false positive suppression mechanism for the checker. The
182 // annotation is allowed on properties and ivars.
183 if (isAnnotatedToAllowDirectAssignment(PD) ||
184 isAnnotatedToAllowDirectAssignment(D))
Anna Zaks65195642013-01-16 01:36:00 +0000185 return;
Anna Zaks461f2392012-09-27 19:45:15 +0000186
187 ObjCMethodDecl *GetterMethod =
188 InterfD->getInstanceMethod(PD->getGetterName());
189 ObjCMethodDecl *SetterMethod =
190 InterfD->getInstanceMethod(PD->getSetterName());
191
192 if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)
193 return;
194
195 if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
196 return;
197
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000198 BR.EmitBasicReport(
199 MD, Checker, "Property access", categories::CoreFoundationObjectiveC,
Anna Zaks461f2392012-09-27 19:45:15 +0000200 "Direct assignment to an instance variable backing a property; "
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000201 "use the setter instead",
202 PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));
Anna Zaks461f2392012-09-27 19:45:15 +0000203 }
204 }
205}
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000206}
Anna Zaks461f2392012-09-27 19:45:15 +0000207
Anna Zaks25dd07c2012-12-05 01:14:37 +0000208// Register the checker that checks for direct accesses in functions annotated
Ted Kremenekc6324672012-12-22 00:34:48 +0000209// with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).
Benjamin Kramer27bc5042013-08-09 17:17:42 +0000210static bool AttrFilter(const ObjCMethodDecl *M) {
Aaron Ballmanbe22bcb2014-03-10 17:08:28 +0000211 for (const auto *Ann : M->specific_attrs<AnnotateAttr>())
Benjamin Kramer27bc5042013-08-09 17:17:42 +0000212 if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")
213 return false;
Benjamin Kramer27bc5042013-08-09 17:17:42 +0000214 return true;
Anna Zaks25dd07c2012-12-05 01:14:37 +0000215}
216
Kristof Umann058a7a42019-01-26 14:23:08 +0000217// Register the checker that checks for direct accesses in all functions,
218// except for the initialization and copy routines.
219void ento::registerDirectIvarAssignment(CheckerManager &mgr) {
220 mgr.registerChecker<DirectIvarAssignment>();
221}
222
223bool ento::shouldRegisterDirectIvarAssignment(const LangOptions &LO) {
224 return true;
225}
226
Anna Zaks25dd07c2012-12-05 01:14:37 +0000227void ento::registerDirectIvarAssignmentForAnnotatedFunctions(
228 CheckerManager &mgr) {
Kristof Umann204bf2b2019-01-26 21:41:50 +0000229 mgr.getChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter;
Anna Zaks25dd07c2012-12-05 01:14:37 +0000230}
Kristof Umann058a7a42019-01-26 14:23:08 +0000231
232bool ento::shouldRegisterDirectIvarAssignmentForAnnotatedFunctions(
233 const LangOptions &LO) {
234 return true;
235}