/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- DynamicTypePropagation.cpp ------------------------------*- 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 | | // This file contains two checkers. One helps the static analyzer core to track |
10 | | // types, the other does type inference on Obj-C generics and report type |
11 | | // errors. |
12 | | // |
13 | | // Dynamic Type Propagation: |
14 | | // This checker defines the rules for dynamic type gathering and propagation. |
15 | | // |
16 | | // Generics Checker for Objective-C: |
17 | | // This checker tries to find type errors that the compiler is not able to catch |
18 | | // due to the implicit conversions that were introduced for backward |
19 | | // compatibility. |
20 | | // |
21 | | //===----------------------------------------------------------------------===// |
22 | | |
23 | | #include "clang/AST/ParentMap.h" |
24 | | #include "clang/AST/RecursiveASTVisitor.h" |
25 | | #include "clang/Basic/Builtins.h" |
26 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
27 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
28 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
29 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
30 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
31 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
32 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" |
33 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
34 | | #include "llvm/ADT/STLExtras.h" |
35 | | #include <optional> |
36 | | |
37 | | using namespace clang; |
38 | | using namespace ento; |
39 | | |
40 | | // ProgramState trait - The type inflation is tracked by DynamicTypeMap. This is |
41 | | // an auxiliary map that tracks more information about generic types, because in |
42 | | // some cases the most derived type is not the most informative one about the |
43 | | // type parameters. This types that are stored for each symbol in this map must |
44 | | // be specialized. |
45 | | // TODO: In some case the type stored in this map is exactly the same that is |
46 | | // stored in DynamicTypeMap. We should no store duplicated information in those |
47 | | // cases. |
48 | | REGISTER_MAP_WITH_PROGRAMSTATE(MostSpecializedTypeArgsMap, SymbolRef, |
49 | | const ObjCObjectPointerType *) |
50 | | |
51 | | namespace { |
52 | | class DynamicTypePropagation: |
53 | | public Checker< check::PreCall, |
54 | | check::PostCall, |
55 | | check::DeadSymbols, |
56 | | check::PostStmt<CastExpr>, |
57 | | check::PostStmt<CXXNewExpr>, |
58 | | check::PreObjCMessage, |
59 | | check::PostObjCMessage > { |
60 | | |
61 | | /// Return a better dynamic type if one can be derived from the cast. |
62 | | const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, |
63 | | CheckerContext &C) const; |
64 | | |
65 | | ExplodedNode *dynamicTypePropagationOnCasts(const CastExpr *CE, |
66 | | ProgramStateRef &State, |
67 | | CheckerContext &C) const; |
68 | | |
69 | | mutable std::unique_ptr<BugType> ObjCGenericsBugType; |
70 | 72 | void initBugType() const { |
71 | 72 | if (!ObjCGenericsBugType) |
72 | 2 | ObjCGenericsBugType.reset(new BugType( |
73 | 2 | GenericCheckName, "Generics", categories::CoreFoundationObjectiveC)); |
74 | 72 | } |
75 | | |
76 | | class GenericsBugVisitor : public BugReporterVisitor { |
77 | | public: |
78 | 72 | GenericsBugVisitor(SymbolRef S) : Sym(S) {} |
79 | | |
80 | 72 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
81 | 72 | static int X = 0; |
82 | 72 | ID.AddPointer(&X); |
83 | 72 | ID.AddPointer(Sym); |
84 | 72 | } |
85 | | |
86 | | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
87 | | BugReporterContext &BRC, |
88 | | PathSensitiveBugReport &BR) override; |
89 | | |
90 | | private: |
91 | | // The tracked symbol. |
92 | | SymbolRef Sym; |
93 | | }; |
94 | | |
95 | | void reportGenericsBug(const ObjCObjectPointerType *From, |
96 | | const ObjCObjectPointerType *To, ExplodedNode *N, |
97 | | SymbolRef Sym, CheckerContext &C, |
98 | | const Stmt *ReportedNode = nullptr) const; |
99 | | |
100 | | public: |
101 | | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
102 | | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
103 | | void checkPostStmt(const CastExpr *CastE, CheckerContext &C) const; |
104 | | void checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const; |
105 | | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; |
106 | | void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; |
107 | | void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; |
108 | | |
109 | | /// This value is set to true, when the Generics checker is turned on. |
110 | | bool CheckGenerics = false; |
111 | | CheckerNameRef GenericCheckName; |
112 | | }; |
113 | | |
114 | 31 | bool isObjCClassType(QualType Type) { |
115 | 31 | if (const auto *PointerType = dyn_cast<ObjCObjectPointerType>(Type)) { |
116 | 31 | return PointerType->getObjectType()->isObjCClass(); |
117 | 31 | } |
118 | 0 | return false; |
119 | 31 | } |
120 | | |
121 | | struct RuntimeType { |
122 | | const ObjCObjectType *Type = nullptr; |
123 | | bool Precise = false; |
124 | | |
125 | 693 | operator bool() const { return Type != nullptr; } |
126 | | }; |
127 | | |
128 | | RuntimeType inferReceiverType(const ObjCMethodCall &Message, |
129 | 693 | CheckerContext &C) { |
130 | 693 | const ObjCMessageExpr *MessageExpr = Message.getOriginExpr(); |
131 | | |
132 | | // Check if we can statically infer the actual type precisely. |
133 | | // |
134 | | // 1. Class is written directly in the message: |
135 | | // \code |
136 | | // [ActualClass classMethod]; |
137 | | // \endcode |
138 | 693 | if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class) { |
139 | 653 | return {MessageExpr->getClassReceiver()->getAs<ObjCObjectType>(), |
140 | 653 | /*Precise=*/true}; |
141 | 653 | } |
142 | | |
143 | | // 2. Receiver is 'super' from a class method (a.k.a 'super' is a |
144 | | // class object). |
145 | | // \code |
146 | | // [super classMethod]; |
147 | | // \endcode |
148 | 40 | if (MessageExpr->getReceiverKind() == ObjCMessageExpr::SuperClass) { |
149 | 7 | return {MessageExpr->getSuperType()->getAs<ObjCObjectType>(), |
150 | 7 | /*Precise=*/true}; |
151 | 7 | } |
152 | | |
153 | | // 3. Receiver is 'super' from an instance method (a.k.a 'super' is an |
154 | | // instance of a super class). |
155 | | // \code |
156 | | // [super instanceMethod]; |
157 | | // \encode |
158 | 33 | if (MessageExpr->getReceiverKind() == ObjCMessageExpr::SuperInstance) { |
159 | 2 | if (const auto *ObjTy = |
160 | 2 | MessageExpr->getSuperType()->getAs<ObjCObjectPointerType>()) |
161 | 2 | return {ObjTy->getObjectType(), /*Precise=*/true}; |
162 | 2 | } |
163 | | |
164 | 31 | const Expr *RecE = MessageExpr->getInstanceReceiver(); |
165 | | |
166 | 31 | if (!RecE) |
167 | 0 | return {}; |
168 | | |
169 | | // Otherwise, let's try to get type information from our estimations of |
170 | | // runtime types. |
171 | 31 | QualType InferredType; |
172 | 31 | SVal ReceiverSVal = C.getSVal(RecE); |
173 | 31 | ProgramStateRef State = C.getState(); |
174 | | |
175 | 31 | if (const MemRegion *ReceiverRegion = ReceiverSVal.getAsRegion()) { |
176 | 31 | if (DynamicTypeInfo DTI = getDynamicTypeInfo(State, ReceiverRegion)) { |
177 | 31 | InferredType = DTI.getType().getCanonicalType(); |
178 | 31 | } |
179 | 31 | } |
180 | | |
181 | 31 | if (SymbolRef ReceiverSymbol = ReceiverSVal.getAsSymbol()) { |
182 | 31 | if (InferredType.isNull()) { |
183 | 0 | InferredType = ReceiverSymbol->getType(); |
184 | 0 | } |
185 | | |
186 | | // If receiver is a Class object, we want to figure out the type it |
187 | | // represents. |
188 | 31 | if (isObjCClassType(InferredType)) { |
189 | | // We actually might have some info on what type is contained in there. |
190 | 20 | if (DynamicTypeInfo DTI = |
191 | 20 | getClassObjectDynamicTypeInfo(State, ReceiverSymbol)) { |
192 | | |
193 | | // Types in Class objects can be ONLY Objective-C types |
194 | 6 | return {cast<ObjCObjectType>(DTI.getType()), !DTI.canBeASubClass()}; |
195 | 6 | } |
196 | | |
197 | 14 | SVal SelfSVal = State->getSelfSVal(C.getLocationContext()); |
198 | | |
199 | | // Another way we can guess what is in Class object, is when it is a |
200 | | // 'self' variable of the current class method. |
201 | 14 | if (ReceiverSVal == SelfSVal) { |
202 | | // In this case, we should return the type of the enclosing class |
203 | | // declaration. |
204 | 14 | if (const ObjCMethodDecl *MD = |
205 | 14 | dyn_cast<ObjCMethodDecl>(C.getStackFrame()->getDecl())) |
206 | 14 | if (const ObjCObjectType *ObjTy = dyn_cast<ObjCObjectType>( |
207 | 14 | MD->getClassInterface()->getTypeForDecl())) |
208 | 14 | return {ObjTy}; |
209 | 14 | } |
210 | 14 | } |
211 | 31 | } |
212 | | |
213 | | // Unfortunately, it seems like we have no idea what that type is. |
214 | 11 | if (InferredType.isNull()) { |
215 | 0 | return {}; |
216 | 0 | } |
217 | | |
218 | | // We can end up here if we got some dynamic type info and the |
219 | | // receiver is not one of the known Class objects. |
220 | 11 | if (const auto *ReceiverInferredType = |
221 | 11 | dyn_cast<ObjCObjectPointerType>(InferredType)) { |
222 | 11 | return {ReceiverInferredType->getObjectType()}; |
223 | 11 | } |
224 | | |
225 | | // Any other type (like 'Class') is not really useful at this point. |
226 | 0 | return {}; |
227 | 11 | } |
228 | | } // end anonymous namespace |
229 | | |
230 | | void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR, |
231 | 405k | CheckerContext &C) const { |
232 | 405k | ProgramStateRef State = removeDeadTypes(C.getState(), SR); |
233 | 405k | State = removeDeadClassObjectTypes(State, SR); |
234 | | |
235 | 405k | MostSpecializedTypeArgsMapTy TyArgMap = |
236 | 405k | State->get<MostSpecializedTypeArgsMap>(); |
237 | 405k | for (SymbolRef Sym : llvm::make_first_range(TyArgMap)) { |
238 | 552 | if (SR.isDead(Sym)) { |
239 | 100 | State = State->remove<MostSpecializedTypeArgsMap>(Sym); |
240 | 100 | } |
241 | 552 | } |
242 | | |
243 | 405k | C.addTransition(State); |
244 | 405k | } |
245 | | |
246 | | static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, |
247 | 1.58k | CheckerContext &C) { |
248 | 1.58k | assert(Region); |
249 | 1.58k | assert(MD); |
250 | | |
251 | 1.58k | ASTContext &Ctx = C.getASTContext(); |
252 | 1.58k | QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent())); |
253 | | |
254 | 1.58k | ProgramStateRef State = C.getState(); |
255 | 1.58k | State = setDynamicTypeInfo(State, Region, Ty, /*CanBeSubClassed=*/false); |
256 | 1.58k | C.addTransition(State); |
257 | 1.58k | } |
258 | | |
259 | | void DynamicTypePropagation::checkPreCall(const CallEvent &Call, |
260 | 102k | CheckerContext &C) const { |
261 | 102k | if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { |
262 | | // C++11 [class.cdtor]p4: When a virtual function is called directly or |
263 | | // indirectly from a constructor or from a destructor, including during |
264 | | // the construction or destruction of the class's non-static data members, |
265 | | // and the object to which the call applies is the object under |
266 | | // construction or destruction, the function called is the final overrider |
267 | | // in the constructor's or destructor's class and not one overriding it in |
268 | | // a more-derived class. |
269 | | |
270 | 23.4k | switch (Ctor->getOriginExpr()->getConstructionKind()) { |
271 | 22.7k | case CXXConstructExpr::CK_Complete: |
272 | 22.7k | case CXXConstructExpr::CK_Delegating: |
273 | | // No additional type info necessary. |
274 | 22.7k | return; |
275 | 628 | case CXXConstructExpr::CK_NonVirtualBase: |
276 | 744 | case CXXConstructExpr::CK_VirtualBase: |
277 | 744 | if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) |
278 | 744 | recordFixedType(Target, Ctor->getDecl(), C); |
279 | 744 | return; |
280 | 23.4k | } |
281 | | |
282 | 0 | return; |
283 | 23.4k | } |
284 | | |
285 | 79.0k | if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { |
286 | | // C++11 [class.cdtor]p4 (see above) |
287 | 1.71k | if (!Dtor->isBaseDestructor()) |
288 | 1.59k | return; |
289 | | |
290 | 121 | const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion(); |
291 | 121 | if (!Target) |
292 | 0 | return; |
293 | | |
294 | 121 | const Decl *D = Dtor->getDecl(); |
295 | 121 | if (!D) |
296 | 0 | return; |
297 | | |
298 | 121 | recordFixedType(Target, cast<CXXDestructorDecl>(D), C); |
299 | 121 | return; |
300 | 121 | } |
301 | 79.0k | } |
302 | | |
303 | | void DynamicTypePropagation::checkPostCall(const CallEvent &Call, |
304 | 108k | CheckerContext &C) const { |
305 | | // We can obtain perfect type info for return values from some calls. |
306 | 108k | if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { |
307 | | |
308 | | // Get the returned value if it's a region. |
309 | 3.70k | const MemRegion *RetReg = Call.getReturnValue().getAsRegion(); |
310 | 3.70k | if (!RetReg) |
311 | 1.24k | return; |
312 | | |
313 | 2.46k | ProgramStateRef State = C.getState(); |
314 | 2.46k | const ObjCMethodDecl *D = Msg->getDecl(); |
315 | | |
316 | 2.46k | if (D && D->hasRelatedResultType()2.44k ) { |
317 | 1.71k | switch (Msg->getMethodFamily()) { |
318 | 319 | default: |
319 | 319 | break; |
320 | | |
321 | | // We assume that the type of the object returned by alloc and new are the |
322 | | // pointer to the object of the class specified in the receiver of the |
323 | | // message. |
324 | 659 | case OMF_alloc: |
325 | 663 | case OMF_new: { |
326 | | // Get the type of object that will get created. |
327 | 663 | RuntimeType ObjTy = inferReceiverType(*Msg, C); |
328 | | |
329 | 663 | if (!ObjTy) |
330 | 0 | return; |
331 | | |
332 | 663 | QualType DynResTy = |
333 | 663 | C.getASTContext().getObjCObjectPointerType(QualType(ObjTy.Type, 0)); |
334 | | // We used to assume that whatever type we got from inferring the |
335 | | // type is actually precise (and it is not exactly correct). |
336 | | // A big portion of the existing behavior depends on that assumption |
337 | | // (e.g. certain inlining won't take place). For this reason, we don't |
338 | | // use ObjTy.Precise flag here. |
339 | | // |
340 | | // TODO: We should mitigate this problem some time in the future |
341 | | // and replace hardcoded 'false' with '!ObjTy.Precise'. |
342 | 663 | C.addTransition(setDynamicTypeInfo(State, RetReg, DynResTy, false)); |
343 | 663 | break; |
344 | 663 | } |
345 | 737 | case OMF_init: { |
346 | | // Assume, the result of the init method has the same dynamic type as |
347 | | // the receiver and propagate the dynamic type info. |
348 | 737 | const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); |
349 | 737 | if (!RecReg) |
350 | 0 | return; |
351 | 737 | DynamicTypeInfo RecDynType = getDynamicTypeInfo(State, RecReg); |
352 | 737 | C.addTransition(setDynamicTypeInfo(State, RetReg, RecDynType)); |
353 | 737 | break; |
354 | 737 | } |
355 | 1.71k | } |
356 | 1.71k | } |
357 | 2.46k | return; |
358 | 2.46k | } |
359 | | |
360 | 104k | if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { |
361 | | // We may need to undo the effects of our pre-call check. |
362 | 23.4k | switch (Ctor->getOriginExpr()->getConstructionKind()) { |
363 | 22.6k | case CXXConstructExpr::CK_Complete: |
364 | 22.6k | case CXXConstructExpr::CK_Delegating: |
365 | | // No additional work necessary. |
366 | | // Note: This will leave behind the actual type of the object for |
367 | | // complete constructors, but arguably that's a good thing, since it |
368 | | // means the dynamic type info will be correct even for objects |
369 | | // constructed with operator new. |
370 | 22.6k | return; |
371 | 626 | case CXXConstructExpr::CK_NonVirtualBase: |
372 | 742 | case CXXConstructExpr::CK_VirtualBase: |
373 | 742 | if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) { |
374 | | // We just finished a base constructor. Now we can use the subclass's |
375 | | // type when resolving virtual calls. |
376 | 742 | const LocationContext *LCtx = C.getLocationContext(); |
377 | | |
378 | | // FIXME: In C++17 classes with non-virtual bases may be treated as |
379 | | // aggregates, and in such case no top-frame constructor will be called. |
380 | | // Figure out if we need to do anything in this case. |
381 | | // FIXME: Instead of relying on the ParentMap, we should have the |
382 | | // trigger-statement (InitListExpr in this case) available in this |
383 | | // callback, ideally as part of CallEvent. |
384 | 742 | if (isa_and_nonnull<InitListExpr>( |
385 | 742 | LCtx->getParentMap().getParent(Ctor->getOriginExpr()))) |
386 | 24 | return; |
387 | | |
388 | 718 | recordFixedType(Target, cast<CXXConstructorDecl>(LCtx->getDecl()), C); |
389 | 718 | } |
390 | 718 | return; |
391 | 23.4k | } |
392 | 23.4k | } |
393 | 104k | } |
394 | | |
395 | | /// TODO: Handle explicit casts. |
396 | | /// Handle C++ casts. |
397 | | /// |
398 | | /// Precondition: the cast is between ObjCObjectPointers. |
399 | | ExplodedNode *DynamicTypePropagation::dynamicTypePropagationOnCasts( |
400 | 1.18k | const CastExpr *CE, ProgramStateRef &State, CheckerContext &C) const { |
401 | | // We only track type info for regions. |
402 | 1.18k | const MemRegion *ToR = C.getSVal(CE).getAsRegion(); |
403 | 1.18k | if (!ToR) |
404 | 157 | return C.getPredecessor(); |
405 | | |
406 | 1.03k | if (isa<ExplicitCastExpr>(CE)) |
407 | 18 | return C.getPredecessor(); |
408 | | |
409 | 1.01k | if (const Type *NewTy = getBetterObjCType(CE, C)) { |
410 | 226 | State = setDynamicTypeInfo(State, ToR, QualType(NewTy, 0)); |
411 | 226 | return C.addTransition(State); |
412 | 226 | } |
413 | 786 | return C.getPredecessor(); |
414 | 1.01k | } |
415 | | |
416 | | void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, |
417 | 1.00k | CheckerContext &C) const { |
418 | 1.00k | if (NewE->isArray()) |
419 | 166 | return; |
420 | | |
421 | | // We only track dynamic type info for regions. |
422 | 839 | const MemRegion *MR = C.getSVal(NewE).getAsRegion(); |
423 | 839 | if (!MR) |
424 | 6 | return; |
425 | | |
426 | 833 | C.addTransition(setDynamicTypeInfo(C.getState(), MR, NewE->getType(), |
427 | 833 | /*CanBeSubClassed=*/false)); |
428 | 833 | } |
429 | | |
430 | | // Return a better dynamic type if one can be derived from the cast. |
431 | | // Compare the current dynamic type of the region and the new type to which we |
432 | | // are casting. If the new type is lower in the inheritance hierarchy, pick it. |
433 | | const ObjCObjectPointerType * |
434 | | DynamicTypePropagation::getBetterObjCType(const Expr *CastE, |
435 | 1.01k | CheckerContext &C) const { |
436 | 1.01k | const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); |
437 | 1.01k | assert(ToR); |
438 | | |
439 | | // Get the old and new types. |
440 | 1.01k | const ObjCObjectPointerType *NewTy = |
441 | 1.01k | CastE->getType()->getAs<ObjCObjectPointerType>(); |
442 | 1.01k | if (!NewTy) |
443 | 0 | return nullptr; |
444 | 1.01k | QualType OldDTy = getDynamicTypeInfo(C.getState(), ToR).getType(); |
445 | 1.01k | if (OldDTy.isNull()) { |
446 | 0 | return NewTy; |
447 | 0 | } |
448 | 1.01k | const ObjCObjectPointerType *OldTy = |
449 | 1.01k | OldDTy->getAs<ObjCObjectPointerType>(); |
450 | 1.01k | if (!OldTy) |
451 | 118 | return nullptr; |
452 | | |
453 | | // Id the old type is 'id', the new one is more precise. |
454 | 894 | if (OldTy->isObjCIdType() && !NewTy->isObjCIdType()136 ) |
455 | 136 | return NewTy; |
456 | | |
457 | | // Return new if it's a subclass of old. |
458 | 758 | const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl(); |
459 | 758 | const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl(); |
460 | 758 | if (ToI && FromI191 && FromI->isSuperClassOf(ToI)191 ) |
461 | 90 | return NewTy; |
462 | | |
463 | 668 | return nullptr; |
464 | 758 | } |
465 | | |
466 | | static const ObjCObjectPointerType *getMostInformativeDerivedClassImpl( |
467 | | const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, |
468 | 120 | const ObjCObjectPointerType *MostInformativeCandidate, ASTContext &C) { |
469 | | // Checking if from and to are the same classes modulo specialization. |
470 | 120 | if (From->getInterfaceDecl()->getCanonicalDecl() == |
471 | 120 | To->getInterfaceDecl()->getCanonicalDecl()) { |
472 | 80 | if (To->isSpecialized()) { |
473 | 38 | assert(MostInformativeCandidate->isSpecialized()); |
474 | 38 | return MostInformativeCandidate; |
475 | 38 | } |
476 | 42 | return From; |
477 | 80 | } |
478 | | |
479 | 40 | if (To->getObjectType()->getSuperClassType().isNull()) { |
480 | | // If To has no super class and From and To aren't the same then |
481 | | // To was not actually a descendent of From. In this case the best we can |
482 | | // do is 'From'. |
483 | 2 | return From; |
484 | 2 | } |
485 | | |
486 | 38 | const auto *SuperOfTo = |
487 | 38 | To->getObjectType()->getSuperClassType()->castAs<ObjCObjectType>(); |
488 | 38 | assert(SuperOfTo); |
489 | 38 | QualType SuperPtrOfToQual = |
490 | 38 | C.getObjCObjectPointerType(QualType(SuperOfTo, 0)); |
491 | 38 | const auto *SuperPtrOfTo = SuperPtrOfToQual->castAs<ObjCObjectPointerType>(); |
492 | 38 | if (To->isUnspecialized()) |
493 | 14 | return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo, SuperPtrOfTo, |
494 | 14 | C); |
495 | 24 | else |
496 | 24 | return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo, |
497 | 24 | MostInformativeCandidate, C); |
498 | 38 | } |
499 | | |
500 | | /// A downcast may loose specialization information. E. g.: |
501 | | /// MutableMap<T, U> : Map |
502 | | /// The downcast to MutableMap looses the information about the types of the |
503 | | /// Map (due to the type parameters are not being forwarded to Map), and in |
504 | | /// general there is no way to recover that information from the |
505 | | /// declaration. In order to have to most information, lets find the most |
506 | | /// derived type that has all the type parameters forwarded. |
507 | | /// |
508 | | /// Get the a subclass of \p From (which has a lower bound \p To) that do not |
509 | | /// loose information about type parameters. \p To has to be a subclass of |
510 | | /// \p From. From has to be specialized. |
511 | | static const ObjCObjectPointerType * |
512 | | getMostInformativeDerivedClass(const ObjCObjectPointerType *From, |
513 | 82 | const ObjCObjectPointerType *To, ASTContext &C) { |
514 | 82 | return getMostInformativeDerivedClassImpl(From, To, To, C); |
515 | 82 | } |
516 | | |
517 | | /// Inputs: |
518 | | /// \param StaticLowerBound Static lower bound for a symbol. The dynamic lower |
519 | | /// bound might be the subclass of this type. |
520 | | /// \param StaticUpperBound A static upper bound for a symbol. |
521 | | /// \p StaticLowerBound expected to be the subclass of \p StaticUpperBound. |
522 | | /// \param Current The type that was inferred for a symbol in a previous |
523 | | /// context. Might be null when this is the first time that inference happens. |
524 | | /// Precondition: |
525 | | /// \p StaticLowerBound or \p StaticUpperBound is specialized. If \p Current |
526 | | /// is not null, it is specialized. |
527 | | /// Possible cases: |
528 | | /// (1) The \p Current is null and \p StaticLowerBound <: \p StaticUpperBound |
529 | | /// (2) \p StaticLowerBound <: \p Current <: \p StaticUpperBound |
530 | | /// (3) \p Current <: \p StaticLowerBound <: \p StaticUpperBound |
531 | | /// (4) \p StaticLowerBound <: \p StaticUpperBound <: \p Current |
532 | | /// Effect: |
533 | | /// Use getMostInformativeDerivedClass with the upper and lower bound of the |
534 | | /// set {\p StaticLowerBound, \p Current, \p StaticUpperBound}. The computed |
535 | | /// lower bound must be specialized. If the result differs from \p Current or |
536 | | /// \p Current is null, store the result. |
537 | | static bool |
538 | | storeWhenMoreInformative(ProgramStateRef &State, SymbolRef Sym, |
539 | | const ObjCObjectPointerType *const *Current, |
540 | | const ObjCObjectPointerType *StaticLowerBound, |
541 | | const ObjCObjectPointerType *StaticUpperBound, |
542 | 104 | ASTContext &C) { |
543 | | // TODO: The above 4 cases are not exhaustive. In particular, it is possible |
544 | | // for Current to be incomparable with StaticLowerBound, StaticUpperBound, |
545 | | // or both. |
546 | | // |
547 | | // For example, suppose Foo<T> and Bar<T> are unrelated types. |
548 | | // |
549 | | // Foo<T> *f = ... |
550 | | // Bar<T> *b = ... |
551 | | // |
552 | | // id t1 = b; |
553 | | // f = t1; |
554 | | // id t2 = f; // StaticLowerBound is Foo<T>, Current is Bar<T> |
555 | | // |
556 | | // We should either constrain the callers of this function so that the stated |
557 | | // preconditions hold (and assert it) or rewrite the function to expicitly |
558 | | // handle the additional cases. |
559 | | |
560 | | // Precondition |
561 | 104 | assert(StaticUpperBound->isSpecialized() || |
562 | 104 | StaticLowerBound->isSpecialized()); |
563 | 104 | assert(!Current || (*Current)->isSpecialized()); |
564 | | |
565 | | // Case (1) |
566 | 104 | if (!Current) { |
567 | 82 | if (StaticUpperBound->isUnspecialized()) { |
568 | 10 | State = State->set<MostSpecializedTypeArgsMap>(Sym, StaticLowerBound); |
569 | 10 | return true; |
570 | 10 | } |
571 | | // Upper bound is specialized. |
572 | 72 | const ObjCObjectPointerType *WithMostInfo = |
573 | 72 | getMostInformativeDerivedClass(StaticUpperBound, StaticLowerBound, C); |
574 | 72 | State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo); |
575 | 72 | return true; |
576 | 82 | } |
577 | | |
578 | | // Case (3) |
579 | 22 | if (C.canAssignObjCInterfaces(StaticLowerBound, *Current)) { |
580 | 16 | return false; |
581 | 16 | } |
582 | | |
583 | | // Case (4) |
584 | 6 | if (C.canAssignObjCInterfaces(*Current, StaticUpperBound)) { |
585 | | // The type arguments might not be forwarded at any point of inheritance. |
586 | 4 | const ObjCObjectPointerType *WithMostInfo = |
587 | 4 | getMostInformativeDerivedClass(*Current, StaticUpperBound, C); |
588 | 4 | WithMostInfo = |
589 | 4 | getMostInformativeDerivedClass(WithMostInfo, StaticLowerBound, C); |
590 | 4 | if (WithMostInfo == *Current) |
591 | 0 | return false; |
592 | 4 | State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo); |
593 | 4 | return true; |
594 | 4 | } |
595 | | |
596 | | // Case (2) |
597 | 2 | const ObjCObjectPointerType *WithMostInfo = |
598 | 2 | getMostInformativeDerivedClass(*Current, StaticLowerBound, C); |
599 | 2 | if (WithMostInfo != *Current) { |
600 | 0 | State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo); |
601 | 0 | return true; |
602 | 0 | } |
603 | | |
604 | 2 | return false; |
605 | 2 | } |
606 | | |
607 | | /// Type inference based on static type information that is available for the |
608 | | /// cast and the tracked type information for the given symbol. When the tracked |
609 | | /// symbol and the destination type of the cast are unrelated, report an error. |
610 | | void DynamicTypePropagation::checkPostStmt(const CastExpr *CE, |
611 | 418k | CheckerContext &C) const { |
612 | 418k | if (CE->getCastKind() != CK_BitCast) |
613 | 407k | return; |
614 | | |
615 | 10.8k | QualType OriginType = CE->getSubExpr()->getType(); |
616 | 10.8k | QualType DestType = CE->getType(); |
617 | | |
618 | 10.8k | const auto *OrigObjectPtrType = OriginType->getAs<ObjCObjectPointerType>(); |
619 | 10.8k | const auto *DestObjectPtrType = DestType->getAs<ObjCObjectPointerType>(); |
620 | | |
621 | 10.8k | if (!OrigObjectPtrType || !DestObjectPtrType1.39k ) |
622 | 9.68k | return; |
623 | | |
624 | 1.18k | ProgramStateRef State = C.getState(); |
625 | 1.18k | ExplodedNode *AfterTypeProp = dynamicTypePropagationOnCasts(CE, State, C); |
626 | | |
627 | 1.18k | ASTContext &ASTCtxt = C.getASTContext(); |
628 | | |
629 | | // This checker detects the subtyping relationships using the assignment |
630 | | // rules. In order to be able to do this the kindofness must be stripped |
631 | | // first. The checker treats every type as kindof type anyways: when the |
632 | | // tracked type is the subtype of the static type it tries to look up the |
633 | | // methods in the tracked type first. |
634 | 1.18k | OrigObjectPtrType = OrigObjectPtrType->stripObjCKindOfTypeAndQuals(ASTCtxt); |
635 | 1.18k | DestObjectPtrType = DestObjectPtrType->stripObjCKindOfTypeAndQuals(ASTCtxt); |
636 | | |
637 | 1.18k | if (OrigObjectPtrType->isUnspecialized() && |
638 | 1.18k | DestObjectPtrType->isUnspecialized()1.10k ) |
639 | 1.01k | return; |
640 | | |
641 | 168 | SymbolRef Sym = C.getSVal(CE).getAsSymbol(); |
642 | 168 | if (!Sym) |
643 | 0 | return; |
644 | | |
645 | 168 | const ObjCObjectPointerType *const *TrackedType = |
646 | 168 | State->get<MostSpecializedTypeArgsMap>(Sym); |
647 | | |
648 | 168 | if (isa<ExplicitCastExpr>(CE)) { |
649 | | // Treat explicit casts as an indication from the programmer that the |
650 | | // Objective-C type system is not rich enough to express the needed |
651 | | // invariant. In such cases, forget any existing information inferred |
652 | | // about the type arguments. We don't assume the casted-to specialized |
653 | | // type here because the invariant the programmer specifies in the cast |
654 | | // may only hold at this particular program point and not later ones. |
655 | | // We don't want a suppressing cast to require a cascade of casts down the |
656 | | // line. |
657 | 12 | if (TrackedType) { |
658 | 6 | State = State->remove<MostSpecializedTypeArgsMap>(Sym); |
659 | 6 | C.addTransition(State, AfterTypeProp); |
660 | 6 | } |
661 | 12 | return; |
662 | 12 | } |
663 | | |
664 | | // Check which assignments are legal. |
665 | 156 | bool OrigToDest = |
666 | 156 | ASTCtxt.canAssignObjCInterfaces(DestObjectPtrType, OrigObjectPtrType); |
667 | 156 | bool DestToOrig = |
668 | 156 | ASTCtxt.canAssignObjCInterfaces(OrigObjectPtrType, DestObjectPtrType); |
669 | | |
670 | | // The tracked type should be the sub or super class of the static destination |
671 | | // type. When an (implicit) upcast or a downcast happens according to static |
672 | | // types, and there is no subtyping relationship between the tracked and the |
673 | | // static destination types, it indicates an error. |
674 | 156 | if (TrackedType && |
675 | 156 | !ASTCtxt.canAssignObjCInterfaces(DestObjectPtrType, *TrackedType)74 && |
676 | 156 | !ASTCtxt.canAssignObjCInterfaces(*TrackedType, DestObjectPtrType)56 ) { |
677 | 52 | static CheckerProgramPointTag IllegalConv(this, "IllegalConversion"); |
678 | 52 | ExplodedNode *N = C.addTransition(State, AfterTypeProp, &IllegalConv); |
679 | 52 | reportGenericsBug(*TrackedType, DestObjectPtrType, N, Sym, C); |
680 | 52 | return; |
681 | 52 | } |
682 | | |
683 | | // Handle downcasts and upcasts. |
684 | | |
685 | 104 | const ObjCObjectPointerType *LowerBound = DestObjectPtrType; |
686 | 104 | const ObjCObjectPointerType *UpperBound = OrigObjectPtrType; |
687 | 104 | if (OrigToDest && !DestToOrig100 ) |
688 | 26 | std::swap(LowerBound, UpperBound); |
689 | | |
690 | | // The id type is not a real bound. Eliminate it. |
691 | 104 | LowerBound = LowerBound->isObjCIdType() ? UpperBound18 : LowerBound86 ; |
692 | 104 | UpperBound = UpperBound->isObjCIdType() ? LowerBound16 : UpperBound88 ; |
693 | | |
694 | 104 | if (storeWhenMoreInformative(State, Sym, TrackedType, LowerBound, UpperBound, |
695 | 104 | ASTCtxt)) { |
696 | 86 | C.addTransition(State, AfterTypeProp); |
697 | 86 | } |
698 | 104 | } |
699 | | |
700 | 33 | static const Expr *stripCastsAndSugar(const Expr *E) { |
701 | 33 | E = E->IgnoreParenImpCasts(); |
702 | 33 | if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) |
703 | 0 | E = POE->getSyntacticForm()->IgnoreParenImpCasts(); |
704 | 33 | if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) |
705 | 0 | E = OVE->getSourceExpr()->IgnoreParenImpCasts(); |
706 | 33 | return E; |
707 | 33 | } |
708 | | |
709 | 101 | static bool isObjCTypeParamDependent(QualType Type) { |
710 | | // It is illegal to typedef parameterized types inside an interface. Therefore |
711 | | // an Objective-C type can only be dependent on a type parameter when the type |
712 | | // parameter structurally present in the type itself. |
713 | 101 | class IsObjCTypeParamDependentTypeVisitor |
714 | 101 | : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { |
715 | 101 | public: |
716 | 101 | IsObjCTypeParamDependentTypeVisitor() = default; |
717 | 101 | bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { |
718 | 68 | if (isa<ObjCTypeParamDecl>(Type->getDecl())) { |
719 | 68 | Result = true; |
720 | 68 | return false; |
721 | 68 | } |
722 | 0 | return true; |
723 | 68 | } |
724 | | |
725 | 101 | bool Result = false; |
726 | 101 | }; |
727 | | |
728 | 101 | IsObjCTypeParamDependentTypeVisitor Visitor; |
729 | 101 | Visitor.TraverseType(Type); |
730 | 101 | return Visitor.Result; |
731 | 101 | } |
732 | | |
733 | | /// A method might not be available in the interface indicated by the static |
734 | | /// type. However it might be available in the tracked type. In order to |
735 | | /// properly substitute the type parameters we need the declaration context of |
736 | | /// the method. The more specialized the enclosing class of the method is, the |
737 | | /// more likely that the parameter substitution will be successful. |
738 | | static const ObjCMethodDecl * |
739 | | findMethodDecl(const ObjCMessageExpr *MessageExpr, |
740 | 176 | const ObjCObjectPointerType *TrackedType, ASTContext &ASTCtxt) { |
741 | 176 | const ObjCMethodDecl *Method = nullptr; |
742 | | |
743 | 176 | QualType ReceiverType = MessageExpr->getReceiverType(); |
744 | | |
745 | | // Do this "devirtualization" on instance and class methods only. Trust the |
746 | | // static type on super and super class calls. |
747 | 176 | if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Instance || |
748 | 176 | MessageExpr->getReceiverKind() == ObjCMessageExpr::Class0 ) { |
749 | | // When the receiver type is id, Class, or some super class of the tracked |
750 | | // type, look up the method in the tracked type, not in the receiver type. |
751 | | // This way we preserve more information. |
752 | 176 | if (ReceiverType->isObjCIdType() || ReceiverType->isObjCClassType()126 || |
753 | 176 | ASTCtxt.canAssignObjCInterfaces( |
754 | 176 | ReceiverType->castAs<ObjCObjectPointerType>(), TrackedType)) { |
755 | 176 | const ObjCInterfaceDecl *InterfaceDecl = TrackedType->getInterfaceDecl(); |
756 | | // The method might not be found. |
757 | 176 | Selector Sel = MessageExpr->getSelector(); |
758 | 176 | Method = InterfaceDecl->lookupInstanceMethod(Sel); |
759 | 176 | if (!Method) |
760 | 12 | Method = InterfaceDecl->lookupClassMethod(Sel); |
761 | 176 | } |
762 | 176 | } |
763 | | |
764 | | // Fallback to statick method lookup when the one based on the tracked type |
765 | | // failed. |
766 | 176 | return Method ? Method172 : MessageExpr->getMethodDecl()4 ; |
767 | 176 | } |
768 | | |
769 | | /// Get the returned ObjCObjectPointerType by a method based on the tracked type |
770 | | /// information, or null pointer when the returned type is not an |
771 | | /// ObjCObjectPointerType. |
772 | | static QualType getReturnTypeForMethod( |
773 | | const ObjCMethodDecl *Method, ArrayRef<QualType> TypeArgs, |
774 | 70 | const ObjCObjectPointerType *SelfType, ASTContext &C) { |
775 | 70 | QualType StaticResultType = Method->getReturnType(); |
776 | | |
777 | | // Is the return type declared as instance type? |
778 | 70 | if (StaticResultType == C.getObjCInstanceType()) |
779 | 2 | return QualType(SelfType, 0); |
780 | | |
781 | | // Check whether the result type depends on a type parameter. |
782 | 68 | if (!isObjCTypeParamDependent(StaticResultType)) |
783 | 33 | return QualType(); |
784 | | |
785 | 35 | QualType ResultType = StaticResultType.substObjCTypeArgs( |
786 | 35 | C, TypeArgs, ObjCSubstitutionContext::Result); |
787 | | |
788 | 35 | return ResultType; |
789 | 68 | } |
790 | | |
791 | | /// When the receiver has a tracked type, use that type to validate the |
792 | | /// argumments of the message expression and the return value. |
793 | | void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M, |
794 | 3.75k | CheckerContext &C) const { |
795 | 3.75k | ProgramStateRef State = C.getState(); |
796 | 3.75k | SymbolRef Sym = M.getReceiverSVal().getAsSymbol(); |
797 | 3.75k | if (!Sym) |
798 | 966 | return; |
799 | | |
800 | 2.78k | const ObjCObjectPointerType *const *TrackedType = |
801 | 2.78k | State->get<MostSpecializedTypeArgsMap>(Sym); |
802 | 2.78k | if (!TrackedType) |
803 | 2.68k | return; |
804 | | |
805 | | // Get the type arguments from tracked type and substitute type arguments |
806 | | // before do the semantic check. |
807 | | |
808 | 104 | ASTContext &ASTCtxt = C.getASTContext(); |
809 | 104 | const ObjCMessageExpr *MessageExpr = M.getOriginExpr(); |
810 | 104 | const ObjCMethodDecl *Method = |
811 | 104 | findMethodDecl(MessageExpr, *TrackedType, ASTCtxt); |
812 | | |
813 | | // It is possible to call non-existent methods in Obj-C. |
814 | 104 | if (!Method) |
815 | 2 | return; |
816 | | |
817 | | // If the method is declared on a class that has a non-invariant |
818 | | // type parameter, don't warn about parameter mismatches after performing |
819 | | // substitution. This prevents warning when the programmer has purposely |
820 | | // casted the receiver to a super type or unspecialized type but the analyzer |
821 | | // has a more precise tracked type than the programmer intends at the call |
822 | | // site. |
823 | | // |
824 | | // For example, consider NSArray (which has a covariant type parameter) |
825 | | // and NSMutableArray (a subclass of NSArray where the type parameter is |
826 | | // invariant): |
827 | | // NSMutableArray *a = [[NSMutableArray<NSString *> alloc] init; |
828 | | // |
829 | | // [a containsObject:number]; // Safe: -containsObject is defined on NSArray. |
830 | | // NSArray<NSObject *> *other = [a arrayByAddingObject:number] // Safe |
831 | | // |
832 | | // [a addObject:number] // Unsafe: -addObject: is defined on NSMutableArray |
833 | | // |
834 | | |
835 | 102 | const ObjCInterfaceDecl *Interface = Method->getClassInterface(); |
836 | 102 | if (!Interface) |
837 | 0 | return; |
838 | | |
839 | 102 | ObjCTypeParamList *TypeParams = Interface->getTypeParamList(); |
840 | 102 | if (!TypeParams) |
841 | 0 | return; |
842 | | |
843 | 102 | for (ObjCTypeParamDecl *TypeParam : *TypeParams) { |
844 | 102 | if (TypeParam->getVariance() != ObjCTypeParamVariance::Invariant) |
845 | 68 | return; |
846 | 102 | } |
847 | | |
848 | 34 | std::optional<ArrayRef<QualType>> TypeArgs = |
849 | 34 | (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); |
850 | | // This case might happen when there is an unspecialized override of a |
851 | | // specialized method. |
852 | 34 | if (!TypeArgs) |
853 | 0 | return; |
854 | | |
855 | 47 | for (unsigned i = 0; 34 i < Method->param_size(); i++13 ) { |
856 | 33 | const Expr *Arg = MessageExpr->getArg(i); |
857 | 33 | const ParmVarDecl *Param = Method->parameters()[i]; |
858 | | |
859 | 33 | QualType OrigParamType = Param->getType(); |
860 | 33 | if (!isObjCTypeParamDependent(OrigParamType)) |
861 | 0 | continue; |
862 | | |
863 | 33 | QualType ParamType = OrigParamType.substObjCTypeArgs( |
864 | 33 | ASTCtxt, *TypeArgs, ObjCSubstitutionContext::Parameter); |
865 | | // Check if it can be assigned |
866 | 33 | const auto *ParamObjectPtrType = ParamType->getAs<ObjCObjectPointerType>(); |
867 | 33 | const auto *ArgObjectPtrType = |
868 | 33 | stripCastsAndSugar(Arg)->getType()->getAs<ObjCObjectPointerType>(); |
869 | 33 | if (!ParamObjectPtrType || !ArgObjectPtrType) |
870 | 0 | continue; |
871 | | |
872 | | // Check if we have more concrete tracked type that is not a super type of |
873 | | // the static argument type. |
874 | 33 | SVal ArgSVal = M.getArgSVal(i); |
875 | 33 | SymbolRef ArgSym = ArgSVal.getAsSymbol(); |
876 | 33 | if (ArgSym) { |
877 | 33 | const ObjCObjectPointerType *const *TrackedArgType = |
878 | 33 | State->get<MostSpecializedTypeArgsMap>(ArgSym); |
879 | 33 | if (TrackedArgType && |
880 | 33 | ASTCtxt.canAssignObjCInterfaces(ArgObjectPtrType, *TrackedArgType)0 ) { |
881 | 0 | ArgObjectPtrType = *TrackedArgType; |
882 | 0 | } |
883 | 33 | } |
884 | | |
885 | | // Warn when argument is incompatible with the parameter. |
886 | 33 | if (!ASTCtxt.canAssignObjCInterfaces(ParamObjectPtrType, |
887 | 33 | ArgObjectPtrType)) { |
888 | 20 | static CheckerProgramPointTag Tag(this, "ArgTypeMismatch"); |
889 | 20 | ExplodedNode *N = C.addTransition(State, &Tag); |
890 | 20 | reportGenericsBug(ArgObjectPtrType, ParamObjectPtrType, N, Sym, C, Arg); |
891 | 20 | return; |
892 | 20 | } |
893 | 33 | } |
894 | 34 | } |
895 | | |
896 | | /// This callback is used to infer the types for Class variables. This info is |
897 | | /// used later to validate messages that sent to classes. Class variables are |
898 | | /// initialized with by invoking the 'class' method on a class. |
899 | | /// This method is also used to infer the type information for the return |
900 | | /// types. |
901 | | // TODO: right now it only tracks generic types. Extend this to track every |
902 | | // type in the DynamicTypeMap and diagnose type errors! |
903 | | void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, |
904 | 3.65k | CheckerContext &C) const { |
905 | 3.65k | const ObjCMessageExpr *MessageExpr = M.getOriginExpr(); |
906 | | |
907 | 3.65k | SymbolRef RetSym = M.getReturnValue().getAsSymbol(); |
908 | 3.65k | if (!RetSym) |
909 | 1.03k | return; |
910 | | |
911 | 2.62k | Selector Sel = MessageExpr->getSelector(); |
912 | 2.62k | ProgramStateRef State = C.getState(); |
913 | | |
914 | | // Here we try to propagate information on Class objects. |
915 | 2.62k | if (Sel.getAsString() == "class") { |
916 | | // We try to figure out the type from the receiver of the 'class' message. |
917 | 29 | if (RuntimeType ReceiverRuntimeType = inferReceiverType(M, C)) { |
918 | | |
919 | 29 | ReceiverRuntimeType.Type->getSuperClassType(); |
920 | 29 | QualType ReceiverClassType(ReceiverRuntimeType.Type, 0); |
921 | | |
922 | | // We want to consider only precise information on generics. |
923 | 29 | if (ReceiverRuntimeType.Type->isSpecialized() && |
924 | 29 | ReceiverRuntimeType.Precise2 ) { |
925 | 2 | QualType ReceiverClassPointerType = |
926 | 2 | C.getASTContext().getObjCObjectPointerType(ReceiverClassType); |
927 | 2 | const auto *InferredType = |
928 | 2 | ReceiverClassPointerType->castAs<ObjCObjectPointerType>(); |
929 | 2 | State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType); |
930 | 2 | } |
931 | | |
932 | | // Constrain the resulting class object to the inferred type. |
933 | 29 | State = setClassObjectDynamicTypeInfo(State, RetSym, ReceiverClassType, |
934 | 29 | !ReceiverRuntimeType.Precise); |
935 | | |
936 | 29 | C.addTransition(State); |
937 | 29 | return; |
938 | 29 | } |
939 | 29 | } |
940 | | |
941 | 2.59k | if (Sel.getAsString() == "superclass") { |
942 | | // We try to figure out the type from the receiver of the 'superclass' |
943 | | // message. |
944 | 1 | if (RuntimeType ReceiverRuntimeType = inferReceiverType(M, C)) { |
945 | | |
946 | | // Result type would be a super class of the receiver's type. |
947 | 1 | QualType ReceiversSuperClass = |
948 | 1 | ReceiverRuntimeType.Type->getSuperClassType(); |
949 | | |
950 | | // Check if it really had super class. |
951 | | // |
952 | | // TODO: we can probably pay closer attention to cases when the class |
953 | | // object can be 'nil' as the result of such message. |
954 | 1 | if (!ReceiversSuperClass.isNull()) { |
955 | | // Constrain the resulting class object to the inferred type. |
956 | 1 | State = setClassObjectDynamicTypeInfo( |
957 | 1 | State, RetSym, ReceiversSuperClass, !ReceiverRuntimeType.Precise); |
958 | | |
959 | 1 | C.addTransition(State); |
960 | 1 | } |
961 | 1 | return; |
962 | 1 | } |
963 | 1 | } |
964 | | |
965 | | // Tracking for return types. |
966 | 2.59k | SymbolRef RecSym = M.getReceiverSVal().getAsSymbol(); |
967 | 2.59k | if (!RecSym) |
968 | 871 | return; |
969 | | |
970 | 1.71k | const ObjCObjectPointerType *const *TrackedType = |
971 | 1.71k | State->get<MostSpecializedTypeArgsMap>(RecSym); |
972 | 1.71k | if (!TrackedType) |
973 | 1.64k | return; |
974 | | |
975 | 72 | ASTContext &ASTCtxt = C.getASTContext(); |
976 | 72 | const ObjCMethodDecl *Method = |
977 | 72 | findMethodDecl(MessageExpr, *TrackedType, ASTCtxt); |
978 | 72 | if (!Method) |
979 | 2 | return; |
980 | | |
981 | 70 | std::optional<ArrayRef<QualType>> TypeArgs = |
982 | 70 | (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); |
983 | 70 | if (!TypeArgs) |
984 | 0 | return; |
985 | | |
986 | 70 | QualType ResultType = |
987 | 70 | getReturnTypeForMethod(Method, *TypeArgs, *TrackedType, ASTCtxt); |
988 | | // The static type is the same as the deduced type. |
989 | 70 | if (ResultType.isNull()) |
990 | 33 | return; |
991 | | |
992 | 37 | const MemRegion *RetRegion = M.getReturnValue().getAsRegion(); |
993 | 37 | ExplodedNode *Pred = C.getPredecessor(); |
994 | | // When there is an entry available for the return symbol in DynamicTypeMap, |
995 | | // the call was inlined, and the information in the DynamicTypeMap is should |
996 | | // be precise. |
997 | 37 | if (RetRegion && !getRawDynamicTypeInfo(State, RetRegion)) { |
998 | | // TODO: we have duplicated information in DynamicTypeMap and |
999 | | // MostSpecializedTypeArgsMap. We should only store anything in the later if |
1000 | | // the stored data differs from the one stored in the former. |
1001 | 37 | State = setDynamicTypeInfo(State, RetRegion, ResultType, |
1002 | 37 | /*CanBeSubClassed=*/true); |
1003 | 37 | Pred = C.addTransition(State); |
1004 | 37 | } |
1005 | | |
1006 | 37 | const auto *ResultPtrType = ResultType->getAs<ObjCObjectPointerType>(); |
1007 | | |
1008 | 37 | if (!ResultPtrType || ResultPtrType->isUnspecialized()) |
1009 | 19 | return; |
1010 | | |
1011 | | // When the result is a specialized type and it is not tracked yet, track it |
1012 | | // for the result symbol. |
1013 | 18 | if (!State->get<MostSpecializedTypeArgsMap>(RetSym)) { |
1014 | 18 | State = State->set<MostSpecializedTypeArgsMap>(RetSym, ResultPtrType); |
1015 | 18 | C.addTransition(State, Pred); |
1016 | 18 | } |
1017 | 18 | } |
1018 | | |
1019 | | void DynamicTypePropagation::reportGenericsBug( |
1020 | | const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, |
1021 | | ExplodedNode *N, SymbolRef Sym, CheckerContext &C, |
1022 | 72 | const Stmt *ReportedNode) const { |
1023 | 72 | if (!CheckGenerics) |
1024 | 0 | return; |
1025 | | |
1026 | 72 | initBugType(); |
1027 | 72 | SmallString<192> Buf; |
1028 | 72 | llvm::raw_svector_ostream OS(Buf); |
1029 | 72 | OS << "Conversion from value of type '"; |
1030 | 72 | QualType::print(From, Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); |
1031 | 72 | OS << "' to incompatible type '"; |
1032 | 72 | QualType::print(To, Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); |
1033 | 72 | OS << "'"; |
1034 | 72 | auto R = std::make_unique<PathSensitiveBugReport>(*ObjCGenericsBugType, |
1035 | 72 | OS.str(), N); |
1036 | 72 | R->markInteresting(Sym); |
1037 | 72 | R->addVisitor(std::make_unique<GenericsBugVisitor>(Sym)); |
1038 | 72 | if (ReportedNode) |
1039 | 20 | R->addRange(ReportedNode->getSourceRange()); |
1040 | 72 | C.emitReport(std::move(R)); |
1041 | 72 | } |
1042 | | |
1043 | | PathDiagnosticPieceRef DynamicTypePropagation::GenericsBugVisitor::VisitNode( |
1044 | | const ExplodedNode *N, BugReporterContext &BRC, |
1045 | 3.13k | PathSensitiveBugReport &BR) { |
1046 | 3.13k | ProgramStateRef state = N->getState(); |
1047 | 3.13k | ProgramStateRef statePrev = N->getFirstPred()->getState(); |
1048 | | |
1049 | 3.13k | const ObjCObjectPointerType *const *TrackedType = |
1050 | 3.13k | state->get<MostSpecializedTypeArgsMap>(Sym); |
1051 | 3.13k | const ObjCObjectPointerType *const *TrackedTypePrev = |
1052 | 3.13k | statePrev->get<MostSpecializedTypeArgsMap>(Sym); |
1053 | 3.13k | if (!TrackedType) |
1054 | 754 | return nullptr; |
1055 | | |
1056 | 2.38k | if (TrackedTypePrev && *TrackedTypePrev == *TrackedType2.31k ) |
1057 | 2.30k | return nullptr; |
1058 | | |
1059 | | // Retrieve the associated statement. |
1060 | 78 | const Stmt *S = N->getStmtForDiagnostics(); |
1061 | 78 | if (!S) |
1062 | 0 | return nullptr; |
1063 | | |
1064 | 78 | const LangOptions &LangOpts = BRC.getASTContext().getLangOpts(); |
1065 | | |
1066 | 78 | SmallString<256> Buf; |
1067 | 78 | llvm::raw_svector_ostream OS(Buf); |
1068 | 78 | OS << "Type '"; |
1069 | 78 | QualType::print(*TrackedType, Qualifiers(), OS, LangOpts, llvm::Twine()); |
1070 | 78 | OS << "' is inferred from "; |
1071 | | |
1072 | 78 | if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) { |
1073 | 0 | OS << "explicit cast (from '"; |
1074 | 0 | QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(), |
1075 | 0 | Qualifiers(), OS, LangOpts, llvm::Twine()); |
1076 | 0 | OS << "' to '"; |
1077 | 0 | QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS, |
1078 | 0 | LangOpts, llvm::Twine()); |
1079 | 0 | OS << "')"; |
1080 | 78 | } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) { |
1081 | 70 | OS << "implicit cast (from '"; |
1082 | 70 | QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(), |
1083 | 70 | Qualifiers(), OS, LangOpts, llvm::Twine()); |
1084 | 70 | OS << "' to '"; |
1085 | 70 | QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS, |
1086 | 70 | LangOpts, llvm::Twine()); |
1087 | 70 | OS << "')"; |
1088 | 70 | } else { |
1089 | 8 | OS << "this context"; |
1090 | 8 | } |
1091 | | |
1092 | | // Generate the extra diagnostic. |
1093 | 78 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
1094 | 78 | N->getLocationContext()); |
1095 | 78 | return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); |
1096 | 78 | } |
1097 | | |
1098 | | /// Register checkers. |
1099 | 50 | void ento::registerObjCGenericsChecker(CheckerManager &mgr) { |
1100 | 50 | DynamicTypePropagation *checker = mgr.getChecker<DynamicTypePropagation>(); |
1101 | 50 | checker->CheckGenerics = true; |
1102 | 50 | checker->GenericCheckName = mgr.getCurrentCheckerName(); |
1103 | 50 | } |
1104 | | |
1105 | 100 | bool ento::shouldRegisterObjCGenericsChecker(const CheckerManager &mgr) { |
1106 | 100 | return true; |
1107 | 100 | } |
1108 | | |
1109 | 1.27k | void ento::registerDynamicTypePropagation(CheckerManager &mgr) { |
1110 | 1.27k | mgr.registerChecker<DynamicTypePropagation>(); |
1111 | 1.27k | } |
1112 | | |
1113 | 2.65k | bool ento::shouldRegisterDynamicTypePropagation(const CheckerManager &mgr) { |
1114 | 2.65k | return true; |
1115 | 2.65k | } |