clang 10.0.0git

DirectIvarAssignment.cpp
Go to the documentation of this file.
1 //=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==//
2 //
3 // 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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // 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.
20 //
21 //===----------------------------------------------------------------------===//
22
23 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
24 #include "clang/AST/Attr.h"
25 #include "clang/AST/DeclObjC.h"
26 #include "clang/AST/StmtVisitor.h"
27 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
28 #include "clang/StaticAnalyzer/Core/Checker.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
30 #include "llvm/ADT/DenseMap.h"
31
32 using namespace clang;
33 using namespace ento;
34
35 namespace {
36
37 /// 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.
42 static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
43 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;
49 }
50
51 class 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;
64 const CheckerBase *Checker;
65 LocationOrAnalysisDeclContext DCtx;
66
67 public:
68 MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
69 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) {}
73
74 void VisitStmt(const Stmt *S) { VisitChildren(S); }
75
76 void VisitBinaryOperator(const BinaryOperator *BO);
77
78 void VisitChildren(const Stmt *S) {
79 for (const Stmt *Child : S->children())
80 if (Child)
81 this->Visit(Child);
82 }
83 };
84
85 public:
86 bool (*ShouldSkipMethod)(const ObjCMethodDecl *);
87
88 DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {}
89
90 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
91 BugReporter &BR) const;
92 };
93
94 static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD,
95 const ObjCInterfaceDecl *InterD,
96 ASTContext &Ctx) {
97 // Check for synthesized ivars.
98 ObjCIvarDecl *ID = PD->getPropertyIvarDecl();
99 if (ID)
100 return ID;
101
102 ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD);
103
104 // Check for existing "_PropName".
105 ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx));
106 if (ID)
107 return ID;
108
109 // Check for existing "PropName".
110 IdentifierInfo *PropIdent = PD->getIdentifier();
111 ID = NonConstInterD->lookupInstanceVariable(PropIdent);
112
113 return ID;
114 }
115
116 void 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.
125 for (const auto *PD : InterD->instance_properties()) {
126 // Find the corresponding IVar.
127 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,
128 Mgr.getASTContext());
129
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
140 for (const auto *M : D->instance_methods()) {
141 AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
142
143 if ((*ShouldSkipMethod)(M))
144 continue;
145
146 const Stmt *Body = M->getBody();
147 if (M->isSynthesizedAccessorStub())
148 continue;
149 assert(Body);
150
151 MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
152 DCtx);
153 MC.VisitStmt(Body);
154 }
155 }
156
157 static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {
158 for (const auto *Ann : D->specific_attrs<AnnotateAttr>())
159 if (Ann->getAnnotation() ==
160 "objc_allow_direct_instance_variable_assignment")
161 return true;
162 return false;
163 }
164
165 void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
166 const BinaryOperator *BO) {
167 if (!BO->isAssignmentOp())
168 return;
169
170 const ObjCIvarRefExpr *IvarRef =
171 dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());
172
173 if (!IvarRef)
174 return;
175
176 if (const ObjCIvarDecl *D = IvarRef->getDecl()) {
177 IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);
178
179 if (I != IvarToPropMap.end()) {
180 const ObjCPropertyDecl *PD = I->second;
181 // Skip warnings on Ivars, annotated with
182 // objc_allow_direct_instance_variable_assignment. This annotation serves
183 // as a false positive suppression mechanism for the checker. The
184 // annotation is allowed on properties and ivars.
185 if (isAnnotatedToAllowDirectAssignment(PD) ||
186 isAnnotatedToAllowDirectAssignment(D))
187 return;
188
189 ObjCMethodDecl *GetterMethod =
190 InterfD->getInstanceMethod(PD->getGetterName());
191 ObjCMethodDecl *SetterMethod =
192 InterfD->getInstanceMethod(PD->getSetterName());
193
194 if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)
195 return;
196
197 if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
198 return;
199
200 BR.EmitBasicReport(
201 MD, Checker, "Property access", categories::CoreFoundationObjectiveC,
202 "Direct assignment to an instance variable backing a property; "
203 "use the setter instead",
204 PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));
205 }
206 }
207 }
208 }
209
210 // Register the checker that checks for direct accesses in functions annotated
211 // with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).
212 static bool AttrFilter(const ObjCMethodDecl *M) {
213 for (const auto *Ann : M->specific_attrs<AnnotateAttr>())
214 if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")
215 return false;
216 return true;
217 }
218
219 // Register the checker that checks for direct accesses in all functions,
220 // except for the initialization and copy routines.
221 void ento::registerDirectIvarAssignment(CheckerManager &mgr) {
222 mgr.registerChecker<DirectIvarAssignment>();
223 }
224
225 bool ento::shouldRegisterDirectIvarAssignment(const LangOptions &LO) {
226 return true;
227 }
228
229 void ento::registerDirectIvarAssignmentForAnnotatedFunctions(
230 CheckerManager &mgr) {
231 mgr.getChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter;
232 }
233
234 bool ento::shouldRegisterDirectIvarAssignmentForAnnotatedFunctions(
235 const LangOptions &LO) {
236 return true;
237 }
clang::Builtin::ID
ID
Definition: Builtins.h:46
ento
clang::ento::categories::CoreFoundationObjectiveC
const char *const CoreFoundationObjectiveC
Definition: CommonBugCategories.cpp:14
clang::ConstStmtVisitor
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:193
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:66
clang::Decl
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:88
AttrFilter
static bool AttrFilter(const ObjCMethodDecl *M)
Definition: DirectIvarAssignment.cpp:212
DeclObjC.h
clang::ObjCMethodDecl
ObjCMethodDecl - Represents an instance or class method declaration.
Definition: DeclObjC.h:138
clang::BinaryOperator::isAssignmentOp
static bool isAssignmentOp(Opcode Opc)
Definition: Expr.h:3560
clang::NamedDecl::getIdentifier
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:244
clang::ObjCContainerDecl::instance_properties
instprop_range instance_properties() const
Definition: DeclObjC.h:1017
clang::IdentifierInfo
One of these records is kept for each identifier that is lexed.
Definition: IdentifierTable.h:57
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:168
clang::AnalysisDeclContext
AnalysisDeclContext contains the context data for the function or method under analysis.
Definition: AnalysisDeclContext.h:69
clang::ObjCContainerDecl::instance_methods
instmeth_range instance_methods() const
Definition: DeclObjC.h:1068
Checker.h
clang::LangOptions
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:53
clang::ObjCMethodDecl::getMethodFamily
ObjCMethodFamily getMethodFamily() const
Determines the family of this method.
Definition: DeclObjC.cpp:997
clang::Stmt::children
child_range children()
Definition: Stmt.cpp:224
clang::BinaryOperator
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3434
clang::Expr::IgnoreParenCasts
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:2995
clang::ObjCInterfaceDecl
Represents an ObjC class declaration.
Definition: DeclObjC.h:1186
clang::ObjCMethodDecl::getCanonicalDecl
ObjCMethodDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: DeclObjC.cpp:957
clang::ObjCIvarRefExpr::getDecl
ObjCIvarDecl * getDecl()
Definition: ExprObjC.h:576
BugReporter.h
clang::ObjCPropertyDecl::getSetterName
Selector getSetterName() const
Definition: DeclObjC.h:928
clang::ObjCPropertyDecl::getDefaultSynthIvarName
IdentifierInfo * getDefaultSynthIvarName(ASTContext &Ctx) const
Get the default name of the synthesized ivar.
Definition: DeclObjC.cpp:224
clang::OMF_copy
Definition: IdentifierTable.h:632
AnalysisManager.h
clang::OMF_dealloc
Definition: IdentifierTable.h:640
bool
#define bool
Definition: stdbool.h:15
clang::Selector::getNameForSlot
StringRef getNameForSlot(unsigned argIndex) const
Retrieve the name at a given position in the selector.
Definition: IdentifierTable.cpp:452
clang::OMF_mutableCopy
Definition: IdentifierTable.h:634
clang::ObjCInterfaceDecl::lookupInstanceVariable
ObjCIvarDecl * lookupInstanceVariable(IdentifierInfo *IVarName, ObjCInterfaceDecl *&ClassDeclared)
Definition: DeclObjC.cpp:620
StmtVisitor.h
clang::ento::LocationOrAnalysisDeclContext
llvm::PointerUnion< const LocationContext *, AnalysisDeclContext * > LocationOrAnalysisDeclContext
Definition: PathDiagnostic.h:155
clang::ObjCMethodDecl::getSelector
Selector getSelector() const
Definition: DeclObjC.h:322
BuiltinCheckerRegistration.h
clang::ObjCMethodDecl::isSynthesizedAccessorStub
bool isSynthesizedAccessorStub() const
Definition: DeclObjC.h:441
Attr.h
clang::Decl::getASTContext
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:377
clang::ObjCMethodDecl::getBody
Stmt * getBody() const override
Retrieve the body of this method, if it has one.
Definition: DeclObjC.cpp:855
clang::ObjCPropertyDecl
Represents one property declaration in an Objective-C interface.
Definition: DeclObjC.h:741
clang::BinaryOperator::getLHS
Expr * getLHS() const
Definition: Expr.h:3474
clang::ObjCImplDecl::getClassInterface
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2454
clang
Dataflow Directional Tag Classes.
Definition: CFGReachabilityAnalysis.h:21
clang::ObjCImplementationDecl
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2566
clang::ObjCIvarRefExpr
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:546
clang::Decl::specific_attrs
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
Definition: DeclBase.h:524
clang::ObjCIvarDecl
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1959
clang::ObjCPropertyDecl::getPropertyIvarDecl
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:959
clang::OMF_init
Definition: IdentifierTable.h:633
clang::ObjCContainerDecl::getInstanceMethod
ObjCMethodDecl * getInstanceMethod(Selector Sel, bool AllowHidden=false) const
Definition: DeclObjC.h:1101
clang::ObjCPropertyDecl::getGetterName
Selector getGetterName() const
Definition: DeclObjC.h:920