1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
16 | #include "clang/AST/DeclObjC.h" |
17 | #include "clang/AST/Expr.h" |
18 | #include "clang/AST/ExprObjC.h" |
19 | #include "clang/AST/RecursiveASTVisitor.h" |
20 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
21 | #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" |
22 | #include "clang/StaticAnalyzer/Core/Checker.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
24 | #include "llvm/ADT/SmallSet.h" |
25 | #include "llvm/ADT/SmallString.h" |
26 | #include "llvm/Support/raw_ostream.h" |
27 | |
28 | using namespace clang; |
29 | using namespace ento; |
30 | |
31 | namespace { |
32 | struct SelectorDescriptor { |
33 | const char *SelectorName; |
34 | unsigned ArgumentCount; |
35 | }; |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { |
42 | public: |
43 | explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {} |
44 | |
45 | bool VisitObjCMessageExpr(ObjCMessageExpr *E) { |
46 | if (E->getSelector() == Sel) |
47 | if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) |
48 | DoesCallSuper = true; |
49 | |
50 | |
51 | return !DoesCallSuper; |
52 | } |
53 | |
54 | bool DoesCallSuper; |
55 | |
56 | private: |
57 | Selector Sel; |
58 | }; |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | class ObjCSuperCallChecker : public Checker< |
65 | check::ASTDecl<ObjCImplementationDecl> > { |
66 | public: |
67 | ObjCSuperCallChecker() : IsInitialized(false) {} |
68 | |
69 | void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, |
70 | BugReporter &BR) const; |
71 | private: |
72 | bool isCheckableClass(const ObjCImplementationDecl *D, |
73 | StringRef &SuperclassName) const; |
74 | void initializeSelectors(ASTContext &Ctx) const; |
75 | void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, |
76 | StringRef ClassName) const; |
77 | mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass; |
78 | mutable bool IsInitialized; |
79 | }; |
80 | |
81 | } |
82 | |
83 | |
84 | |
85 | |
86 | |
87 | |
88 | bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D, |
89 | StringRef &SuperclassName) const { |
90 | const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass(); |
91 | for ( ; ID ; ID = ID->getSuperClass()) |
92 | { |
93 | SuperclassName = ID->getIdentifier()->getName(); |
94 | if (SelectorsForClass.count(SuperclassName)) |
95 | return true; |
96 | } |
97 | return false; |
98 | } |
99 | |
100 | void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, |
101 | ArrayRef<SelectorDescriptor> Sel, |
102 | StringRef ClassName) const { |
103 | llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; |
104 | |
105 | for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end(); |
106 | I != E; ++I) { |
107 | SelectorDescriptor Descriptor = *I; |
108 | assert(Descriptor.ArgumentCount <= 1); |
109 | |
110 | |
111 | IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName); |
112 | |
113 | Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II); |
114 | ClassSelectors.insert(Sel); |
115 | } |
116 | } |
117 | |
118 | void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const { |
119 | |
120 | { |
121 | const SelectorDescriptor Selectors[] = { |
122 | { "addChildViewController", 1 }, |
123 | { "viewDidAppear", 1 }, |
124 | { "viewDidDisappear", 1 }, |
125 | { "viewWillAppear", 1 }, |
126 | { "viewWillDisappear", 1 }, |
127 | { "removeFromParentViewController", 0 }, |
128 | { "didReceiveMemoryWarning", 0 }, |
129 | { "viewDidUnload", 0 }, |
130 | { "viewDidLoad", 0 }, |
131 | { "viewWillUnload", 0 }, |
132 | { "updateViewConstraints", 0 }, |
133 | { "encodeRestorableStateWithCoder", 1 }, |
134 | { "restoreStateWithCoder", 1 }}; |
135 | |
136 | fillSelectors(Ctx, Selectors, "UIViewController"); |
137 | } |
138 | |
139 | { |
140 | const SelectorDescriptor Selectors[] = { |
141 | { "resignFirstResponder", 0 }}; |
142 | |
143 | fillSelectors(Ctx, Selectors, "UIResponder"); |
144 | } |
145 | |
146 | { |
147 | const SelectorDescriptor Selectors[] = { |
148 | { "encodeRestorableStateWithCoder", 1 }, |
149 | { "restoreStateWithCoder", 1 }}; |
150 | |
151 | fillSelectors(Ctx, Selectors, "NSResponder"); |
152 | } |
153 | |
154 | { |
155 | const SelectorDescriptor Selectors[] = { |
156 | { "encodeRestorableStateWithCoder", 1 }, |
157 | { "restoreStateWithCoder", 1 }}; |
158 | |
159 | fillSelectors(Ctx, Selectors, "NSDocument"); |
160 | } |
161 | |
162 | IsInitialized = true; |
163 | } |
164 | |
165 | void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, |
166 | AnalysisManager &Mgr, |
167 | BugReporter &BR) const { |
168 | ASTContext &Ctx = BR.getContext(); |
169 | |
170 | |
171 | if (!IsInitialized) |
172 | initializeSelectors(Ctx); |
173 | |
174 | |
175 | StringRef SuperclassName; |
176 | if (!isCheckableClass(D, SuperclassName)) |
177 | return; |
178 | |
179 | |
180 | |
181 | for (auto *MD : D->instance_methods()) { |
182 | Selector S = MD->getSelector(); |
183 | |
184 | if (!SelectorsForClass[SuperclassName].count(S)) |
185 | continue; |
186 | |
187 | |
188 | if (MD->getBody()) |
189 | { |
190 | FindSuperCallVisitor Visitor(S); |
191 | Visitor.TraverseDecl(MD); |
192 | |
193 | |
194 | if (!Visitor.DoesCallSuper) { |
195 | PathDiagnosticLocation DLoc = |
196 | PathDiagnosticLocation::createEnd(MD->getBody(), |
197 | BR.getSourceManager(), |
198 | Mgr.getAnalysisDeclContext(D)); |
199 | |
200 | const char *Name = "Missing call to superclass"; |
201 | SmallString<320> Buf; |
202 | llvm::raw_svector_ostream os(Buf); |
203 | |
204 | os << "The '" << S.getAsString() |
205 | << "' instance method in " << SuperclassName.str() << " subclass '" |
206 | << *D << "' is missing a [super " << S.getAsString() << "] call"; |
207 | |
208 | BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC, |
209 | os.str(), DLoc); |
210 | } |
211 | } |
212 | } |
213 | } |
214 | |
215 | |
216 | |
217 | |
218 | |
219 | |
220 | void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { |
221 | Mgr.registerChecker<ObjCSuperCallChecker>(); |
222 | } |
223 | |
224 | bool ento::shouldRegisterObjCSuperCallChecker(const LangOptions &LO) { |
225 | return true; |
226 | } |
227 | |
228 | |
229 | |
230 | |
231 | |
232 | |
233 | |
234 | |
235 | |
236 | |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | |
243 | |
244 | |
245 | |
246 | |
247 | |
248 | |
249 | |
250 | |
251 | |
252 | |
253 | |
254 | |
255 | |
256 | |
257 | |
258 | |
259 | |
260 | |
261 | |
262 | |
263 | |
264 | |
265 | |
266 | |