1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
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 | |
38 | |
39 | |
40 | |
41 | |
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 | |
58 | |
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 | |
98 | ObjCIvarDecl *ID = PD->getPropertyIvarDecl(); |
99 | if (ID) |
100 | return ID; |
101 | |
102 | ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD); |
103 | |
104 | |
105 | ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx)); |
106 | if (ID) |
107 | return ID; |
108 | |
109 | |
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 | |
125 | for (const auto *PD : InterD->instance_properties()) { |
126 | |
127 | const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD, |
128 | Mgr.getASTContext()); |
129 | |
130 | if (!ID) |
131 | continue; |
132 | |
133 | |
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 | |
180 | |
181 | |
182 | |
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 | |
209 | |
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 | |
218 | |
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 | } |
236 | |