clang  10.0.0svn
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 
24 #include "clang/AST/Attr.h"
25 #include "clang/AST/DeclObjC.h"
26 #include "clang/AST/StmtVisitor.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 ||
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;
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  assert(Body);
148 
149  MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
150  DCtx);
151  MC.VisitStmt(Body);
152  }
153 }
154 
155 static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {
156  for (const auto *Ann : D->specific_attrs<AnnotateAttr>())
157  if (Ann->getAnnotation() ==
158  "objc_allow_direct_instance_variable_assignment")
159  return true;
160  return false;
161 }
162 
163 void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
164  const BinaryOperator *BO) {
165  if (!BO->isAssignmentOp())
166  return;
167 
168  const ObjCIvarRefExpr *IvarRef =
169  dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());
170 
171  if (!IvarRef)
172  return;
173 
174  if (const ObjCIvarDecl *D = IvarRef->getDecl()) {
175  IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);
176 
177  if (I != IvarToPropMap.end()) {
178  const ObjCPropertyDecl *PD = I->second;
179  // Skip warnings on Ivars, annotated with
180  // objc_allow_direct_instance_variable_assignment. This annotation serves
181  // 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))
185  return;
186 
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 
198  BR.EmitBasicReport(
199  MD, Checker, "Property access", categories::CoreFoundationObjectiveC,
200  "Direct assignment to an instance variable backing a property; "
201  "use the setter instead",
202  PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));
203  }
204  }
205 }
206 }
207 
208 // Register the checker that checks for direct accesses in functions annotated
209 // with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).
210 static bool AttrFilter(const ObjCMethodDecl *M) {
211  for (const auto *Ann : M->specific_attrs<AnnotateAttr>())
212  if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")
213  return false;
214  return true;
215 }
216 
217 // Register the checker that checks for direct accesses in all functions,
218 // except for the initialization and copy routines.
219 void ento::registerDirectIvarAssignment(CheckerManager &mgr) {
220  mgr.registerChecker<DirectIvarAssignment>();
221 }
222 
223 bool ento::shouldRegisterDirectIvarAssignment(const LangOptions &LO) {
224  return true;
225 }
226 
227 void ento::registerDirectIvarAssignmentForAnnotatedFunctions(
228  CheckerManager &mgr) {
229  mgr.getChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter;
230 }
231 
232 bool ento::shouldRegisterDirectIvarAssignmentForAnnotatedFunctions(
233  const LangOptions &LO) {
234  return true;
235 }
const char *const CoreFoundationObjectiveC
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:192
Stmt - This represents one statement.
Definition: Stmt.h:66
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:88
static bool AttrFilter(const ObjCMethodDecl *M)
ObjCMethodDecl - Represents an instance or class method declaration.
Definition: DeclObjC.h:138
static bool isAssignmentOp(Opcode Opc)
Definition: Expr.h:3535
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:269
instprop_range instance_properties() const
Definition: DeclObjC.h:1002
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:160
AnalysisDeclContext contains the context data for the function or method under analysis.
instmeth_range instance_methods() const
Definition: DeclObjC.h:1053
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
ObjCMethodFamily getMethodFamily() const
Determines the family of this method.
Definition: DeclObjC.cpp:985
child_range children()
Definition: Stmt.cpp:223
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3409
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:2971
Represents an ObjC class declaration.
Definition: DeclObjC.h:1171
ObjCMethodDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: DeclObjC.cpp:953
ObjCIvarDecl * getDecl()
Definition: ExprObjC.h:576
Selector getSetterName() const
Definition: DeclObjC.h:913
IdentifierInfo * getDefaultSynthIvarName(ASTContext &Ctx) const
Get the default name of the synthesized ivar.
Definition: DeclObjC.cpp:224
#define bool
Definition: stdbool.h:15
StringRef getNameForSlot(unsigned argIndex) const
Retrieve the name at a given position in the selector.
ObjCIvarDecl * lookupInstanceVariable(IdentifierInfo *IVarName, ObjCInterfaceDecl *&ClassDeclared)
Definition: DeclObjC.cpp:620
llvm::PointerUnion< const LocationContext *, AnalysisDeclContext * > LocationOrAnalysisDeclContext
Selector getSelector() const
Definition: DeclObjC.h:320
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:377
Stmt * getBody() const override
Retrieve the body of this method, if it has one.
Definition: DeclObjC.cpp:851
Represents one property declaration in an Objective-C interface.
Definition: DeclObjC.h:728
Expr * getLHS() const
Definition: Expr.h:3449
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2439
Dataflow Directional Tag Classes.
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2551
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:546
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
Definition: DeclBase.h:524
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1944
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:944
ObjCMethodDecl * getInstanceMethod(Selector Sel, bool AllowHidden=false) const
Definition: DeclObjC.h:1086
Selector getGetterName() const
Definition: DeclObjC.h:905