]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / DirectIvarAssignment.cpp
1 //=- DirectIvarAssignment.cpp - Check rules on 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 //  Check that Objective C properties are set with the setter, not though a
11 //      direct assignment.
12 //
13 //  Two versions of a checker exist: one that checks all methods and the other
14 //      that only checks the methods annotated with
15 //      __attribute__((annotate("objc_no_direct_instance_variable_assignment")))
16 //
17 //  The checker does not warn about assignments to Ivars, annotated with
18 //       __attribute__((objc_allow_direct_instance_variable_assignment"))). This
19 //      annotation serves as a false positive suppression mechanism for the
20 //      checker. The annotation is allowed on properties and Ivars.
21 //
22 //===----------------------------------------------------------------------===//
23
24 #include "ClangSACheckers.h"
25 #include "clang/AST/Attr.h"
26 #include "clang/AST/DeclObjC.h"
27 #include "clang/AST/StmtVisitor.h"
28 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
29 #include "clang/StaticAnalyzer/Core/Checker.h"
30 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
31 #include "llvm/ADT/DenseMap.h"
32
33 using namespace clang;
34 using namespace ento;
35
36 namespace {
37
38 /// The default method filter, which is used to filter out the methods on which
39 /// the check should not be performed.
40 ///
41 /// Checks for the init, dealloc, and any other functions that might be allowed
42 /// to perform direct instance variable assignment based on their name.
43 static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
44   if (M->getMethodFamily() == OMF_init || 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     return true;
50   return false;
51 }
52
53 class DirectIvarAssignment :
54   public Checker<check::ASTDecl<ObjCImplementationDecl> > {
55
56   typedef llvm::DenseMap<const ObjCIvarDecl*,
57                          const ObjCPropertyDecl*> IvarToPropertyMapTy;
58
59   /// A helper class, which walks the AST and locates all assignments to ivars
60   /// in the given function.
61   class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
62     const IvarToPropertyMapTy &IvarToPropMap;
63     const ObjCMethodDecl *MD;
64     const ObjCInterfaceDecl *InterfD;
65     BugReporter &BR;
66     LocationOrAnalysisDeclContext DCtx;
67
68   public:
69     MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
70         const ObjCInterfaceDecl *InID,
71         BugReporter &InBR, AnalysisDeclContext *InDCtx)
72     : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR), 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 (Stmt::const_child_range I = S->children(); I; ++I)
80         if (*I)
81          this->Visit(*I);
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 (ObjCInterfaceDecl::prop_iterator I = InterD->prop_begin(),
126       E = InterD->prop_end(); I != E; ++I) {
127     ObjCPropertyDecl *PD = *I;
128
129     // Find the corresponding IVar.
130     const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,
131                                                      Mgr.getASTContext());
132
133     if (!ID)
134       continue;
135
136     // Store the IVar to property mapping.
137     IvarToPropMap[ID] = PD;
138   }
139
140   if (IvarToPropMap.empty())
141     return;
142
143   for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
144       E = D->instmeth_end(); I != E; ++I) {
145
146     ObjCMethodDecl *M = *I;
147     AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
148
149     if ((*ShouldSkipMethod)(M))
150       continue;
151
152     const Stmt *Body = M->getBody();
153     assert(Body);
154
155     MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, DCtx);
156     MC.VisitStmt(Body);
157   }
158 }
159
160 static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {
161   for (specific_attr_iterator<AnnotateAttr>
162        AI = D->specific_attr_begin<AnnotateAttr>(),
163        AE = D->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
164     const AnnotateAttr *Ann = *AI;
165     if (Ann->getAnnotation() ==
166         "objc_allow_direct_instance_variable_assignment")
167       return true;
168   }
169   return false;
170 }
171
172 void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
173                                                     const BinaryOperator *BO) {
174   if (!BO->isAssignmentOp())
175     return;
176
177   const ObjCIvarRefExpr *IvarRef =
178           dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());
179
180   if (!IvarRef)
181     return;
182
183   if (const ObjCIvarDecl *D = IvarRef->getDecl()) {
184     IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);
185
186     if (I != IvarToPropMap.end()) {
187       const ObjCPropertyDecl *PD = I->second;
188       // Skip warnings on Ivars, annotated with
189       // objc_allow_direct_instance_variable_assignment. This annotation serves
190       // as a false positive suppression mechanism for the checker. The
191       // annotation is allowed on properties and ivars.
192       if (isAnnotatedToAllowDirectAssignment(PD) ||
193           isAnnotatedToAllowDirectAssignment(D))
194         return;
195
196       ObjCMethodDecl *GetterMethod =
197           InterfD->getInstanceMethod(PD->getGetterName());
198       ObjCMethodDecl *SetterMethod =
199           InterfD->getInstanceMethod(PD->getSetterName());
200
201       if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)
202         return;
203
204       if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
205         return;
206
207       BR.EmitBasicReport(MD,
208           "Property access",
209           categories::CoreFoundationObjectiveC,
210           "Direct assignment to an instance variable backing a property; "
211           "use the setter instead", PathDiagnosticLocation(IvarRef,
212                                                           BR.getSourceManager(),
213                                                           DCtx));
214     }
215   }
216 }
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 // Register the checker that checks for direct accesses in functions annotated
226 // with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).
227 static bool AttrFilter(const ObjCMethodDecl *M) {
228   for (specific_attr_iterator<AnnotateAttr>
229            AI = M->specific_attr_begin<AnnotateAttr>(),
230            AE = M->specific_attr_end<AnnotateAttr>();
231        AI != AE; ++AI) {
232     const AnnotateAttr *Ann = *AI;
233     if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")
234       return false;
235   }
236   return true;
237 }
238
239 void ento::registerDirectIvarAssignmentForAnnotatedFunctions(
240     CheckerManager &mgr) {
241   mgr.registerChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter;
242 }