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 | void initBugType() const { |
71 | if (!ObjCGenericsBugType) |
72 | ObjCGenericsBugType.reset(p: new BugType( |
73 | GenericCheckName, "Generics" , categories::CoreFoundationObjectiveC)); |
74 | } |
75 | |
76 | class GenericsBugVisitor : public BugReporterVisitor { |
77 | public: |
78 | GenericsBugVisitor(SymbolRef S) : Sym(S) {} |
79 | |
80 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
81 | static int X = 0; |
82 | ID.AddPointer(Ptr: &X); |
83 | ID.AddPointer(Ptr: Sym); |
84 | } |
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 | bool isObjCClassType(QualType Type) { |
115 | if (const auto *PointerType = dyn_cast<ObjCObjectPointerType>(Val&: Type)) { |
116 | return PointerType->getObjectType()->isObjCClass(); |
117 | } |
118 | return false; |
119 | } |
120 | |
121 | struct RuntimeType { |
122 | const ObjCObjectType *Type = nullptr; |
123 | bool Precise = false; |
124 | |
125 | operator bool() const { return Type != nullptr; } |
126 | }; |
127 | |
128 | RuntimeType inferReceiverType(const ObjCMethodCall &Message, |
129 | CheckerContext &C) { |
130 | 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 | if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class) { |
139 | return {.Type: MessageExpr->getClassReceiver()->getAs<ObjCObjectType>(), |
140 | /*Precise=*/true}; |
141 | } |
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 | if (MessageExpr->getReceiverKind() == ObjCMessageExpr::SuperClass) { |
149 | return {.Type: MessageExpr->getSuperType()->getAs<ObjCObjectType>(), |
150 | /*Precise=*/true}; |
151 | } |
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 | if (MessageExpr->getReceiverKind() == ObjCMessageExpr::SuperInstance) { |
159 | if (const auto *ObjTy = |
160 | MessageExpr->getSuperType()->getAs<ObjCObjectPointerType>()) |
161 | return {.Type: ObjTy->getObjectType(), /*Precise=*/true}; |
162 | } |
163 | |
164 | const Expr *RecE = MessageExpr->getInstanceReceiver(); |
165 | |
166 | if (!RecE) |
167 | return {}; |
168 | |
169 | // Otherwise, let's try to get type information from our estimations of |
170 | // runtime types. |
171 | QualType InferredType; |
172 | SVal ReceiverSVal = C.getSVal(S: RecE); |
173 | ProgramStateRef State = C.getState(); |
174 | |
175 | if (const MemRegion *ReceiverRegion = ReceiverSVal.getAsRegion()) { |
176 | if (DynamicTypeInfo DTI = getDynamicTypeInfo(State, MR: ReceiverRegion)) { |
177 | InferredType = DTI.getType().getCanonicalType(); |
178 | } |
179 | } |
180 | |
181 | if (SymbolRef ReceiverSymbol = ReceiverSVal.getAsSymbol()) { |
182 | if (InferredType.isNull()) { |
183 | InferredType = ReceiverSymbol->getType(); |
184 | } |
185 | |
186 | // If receiver is a Class object, we want to figure out the type it |
187 | // represents. |
188 | if (isObjCClassType(Type: InferredType)) { |
189 | // We actually might have some info on what type is contained in there. |
190 | if (DynamicTypeInfo DTI = |
191 | getClassObjectDynamicTypeInfo(State, Sym: ReceiverSymbol)) { |
192 | |
193 | // Types in Class objects can be ONLY Objective-C types |
194 | return {.Type: cast<ObjCObjectType>(Val: DTI.getType()), .Precise: !DTI.canBeASubClass()}; |
195 | } |
196 | |
197 | SVal SelfSVal = State->getSelfSVal(LC: 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 | if (ReceiverSVal == SelfSVal) { |
202 | // In this case, we should return the type of the enclosing class |
203 | // declaration. |
204 | if (const ObjCMethodDecl *MD = |
205 | dyn_cast<ObjCMethodDecl>(Val: C.getStackFrame()->getDecl())) |
206 | if (const ObjCObjectType *ObjTy = dyn_cast<ObjCObjectType>( |
207 | Val: MD->getClassInterface()->getTypeForDecl())) |
208 | return {.Type: ObjTy}; |
209 | } |
210 | } |
211 | } |
212 | |
213 | // Unfortunately, it seems like we have no idea what that type is. |
214 | if (InferredType.isNull()) { |
215 | return {}; |
216 | } |
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 | if (const auto *ReceiverInferredType = |
221 | dyn_cast<ObjCObjectPointerType>(Val&: InferredType)) { |
222 | return {.Type: ReceiverInferredType->getObjectType()}; |
223 | } |
224 | |
225 | // Any other type (like 'Class') is not really useful at this point. |
226 | return {}; |
227 | } |
228 | } // end anonymous namespace |
229 | |
230 | void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR, |
231 | CheckerContext &C) const { |
232 | ProgramStateRef State = removeDeadTypes(State: C.getState(), SR); |
233 | State = removeDeadClassObjectTypes(State, SR); |
234 | |
235 | MostSpecializedTypeArgsMapTy TyArgMap = |
236 | State->get<MostSpecializedTypeArgsMap>(); |
237 | for (SymbolRef Sym : llvm::make_first_range(c&: TyArgMap)) { |
238 | if (SR.isDead(sym: Sym)) { |
239 | State = State->remove<MostSpecializedTypeArgsMap>(K: Sym); |
240 | } |
241 | } |
242 | |
243 | C.addTransition(State); |
244 | } |
245 | |
246 | static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, |
247 | CheckerContext &C) { |
248 | assert(Region); |
249 | assert(MD); |
250 | |
251 | ASTContext &Ctx = C.getASTContext(); |
252 | QualType Ty = Ctx.getPointerType(T: Ctx.getRecordType(Decl: MD->getParent())); |
253 | |
254 | ProgramStateRef State = C.getState(); |
255 | State = setDynamicTypeInfo(State, MR: Region, NewTy: Ty, /*CanBeSubClassed=*/false); |
256 | C.addTransition(State); |
257 | } |
258 | |
259 | void DynamicTypePropagation::checkPreCall(const CallEvent &Call, |
260 | CheckerContext &C) const { |
261 | if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(Val: &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 | switch (Ctor->getOriginExpr()->getConstructionKind()) { |
271 | case CXXConstructionKind::Complete: |
272 | case CXXConstructionKind::Delegating: |
273 | // No additional type info necessary. |
274 | return; |
275 | case CXXConstructionKind::NonVirtualBase: |
276 | case CXXConstructionKind::VirtualBase: |
277 | if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) |
278 | recordFixedType(Region: Target, MD: Ctor->getDecl(), C); |
279 | return; |
280 | } |
281 | |
282 | return; |
283 | } |
284 | |
285 | if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(Val: &Call)) { |
286 | // C++11 [class.cdtor]p4 (see above) |
287 | if (!Dtor->isBaseDestructor()) |
288 | return; |
289 | |
290 | const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion(); |
291 | if (!Target) |
292 | return; |
293 | |
294 | const Decl *D = Dtor->getDecl(); |
295 | if (!D) |
296 | return; |
297 | |
298 | recordFixedType(Region: Target, MD: cast<CXXDestructorDecl>(Val: D), C); |
299 | return; |
300 | } |
301 | } |
302 | |
303 | void DynamicTypePropagation::checkPostCall(const CallEvent &Call, |
304 | CheckerContext &C) const { |
305 | // We can obtain perfect type info for return values from some calls. |
306 | if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Val: &Call)) { |
307 | |
308 | // Get the returned value if it's a region. |
309 | const MemRegion *RetReg = Call.getReturnValue().getAsRegion(); |
310 | if (!RetReg) |
311 | return; |
312 | |
313 | ProgramStateRef State = C.getState(); |
314 | const ObjCMethodDecl *D = Msg->getDecl(); |
315 | |
316 | if (D && D->hasRelatedResultType()) { |
317 | switch (Msg->getMethodFamily()) { |
318 | default: |
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 | case OMF_alloc: |
325 | case OMF_new: { |
326 | // Get the type of object that will get created. |
327 | RuntimeType ObjTy = inferReceiverType(Message: *Msg, C); |
328 | |
329 | if (!ObjTy) |
330 | return; |
331 | |
332 | QualType DynResTy = |
333 | C.getASTContext().getObjCObjectPointerType(OIT: 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 | C.addTransition(State: setDynamicTypeInfo(State, MR: RetReg, NewTy: DynResTy, CanBeSubClassed: false)); |
343 | break; |
344 | } |
345 | 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 | const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); |
349 | if (!RecReg) |
350 | return; |
351 | DynamicTypeInfo RecDynType = getDynamicTypeInfo(State, MR: RecReg); |
352 | C.addTransition(State: setDynamicTypeInfo(State, MR: RetReg, NewTy: RecDynType)); |
353 | break; |
354 | } |
355 | } |
356 | } |
357 | return; |
358 | } |
359 | |
360 | if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(Val: &Call)) { |
361 | // We may need to undo the effects of our pre-call check. |
362 | switch (Ctor->getOriginExpr()->getConstructionKind()) { |
363 | case CXXConstructionKind::Complete: |
364 | case CXXConstructionKind::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 | return; |
371 | case CXXConstructionKind::NonVirtualBase: |
372 | case CXXConstructionKind::VirtualBase: |
373 | 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 | 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 | if (isa_and_nonnull<InitListExpr>( |
385 | Val: LCtx->getParentMap().getParent(S: Ctor->getOriginExpr()))) |
386 | return; |
387 | |
388 | recordFixedType(Region: Target, MD: cast<CXXConstructorDecl>(Val: LCtx->getDecl()), C); |
389 | } |
390 | return; |
391 | } |
392 | } |
393 | } |
394 | |
395 | /// TODO: Handle explicit casts. |
396 | /// Handle C++ casts. |
397 | /// |
398 | /// Precondition: the cast is between ObjCObjectPointers. |
399 | ExplodedNode *DynamicTypePropagation::dynamicTypePropagationOnCasts( |
400 | const CastExpr *CE, ProgramStateRef &State, CheckerContext &C) const { |
401 | // We only track type info for regions. |
402 | const MemRegion *ToR = C.getSVal(S: CE).getAsRegion(); |
403 | if (!ToR) |
404 | return C.getPredecessor(); |
405 | |
406 | if (isa<ExplicitCastExpr>(Val: CE)) |
407 | return C.getPredecessor(); |
408 | |
409 | if (const Type *NewTy = getBetterObjCType(CastE: CE, C)) { |
410 | State = setDynamicTypeInfo(State, MR: ToR, NewTy: QualType(NewTy, 0)); |
411 | return C.addTransition(State); |
412 | } |
413 | return C.getPredecessor(); |
414 | } |
415 | |
416 | void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, |
417 | CheckerContext &C) const { |
418 | if (NewE->isArray()) |
419 | return; |
420 | |
421 | // We only track dynamic type info for regions. |
422 | const MemRegion *MR = C.getSVal(S: NewE).getAsRegion(); |
423 | if (!MR) |
424 | return; |
425 | |
426 | C.addTransition(State: setDynamicTypeInfo(State: C.getState(), MR, NewTy: NewE->getType(), |
427 | /*CanBeSubClassed=*/false)); |
428 | } |
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 | CheckerContext &C) const { |
436 | const MemRegion *ToR = C.getSVal(S: CastE).getAsRegion(); |
437 | assert(ToR); |
438 | |
439 | // Get the old and new types. |
440 | const ObjCObjectPointerType *NewTy = |
441 | CastE->getType()->getAs<ObjCObjectPointerType>(); |
442 | if (!NewTy) |
443 | return nullptr; |
444 | QualType OldDTy = getDynamicTypeInfo(State: C.getState(), MR: ToR).getType(); |
445 | if (OldDTy.isNull()) { |
446 | return NewTy; |
447 | } |
448 | const ObjCObjectPointerType *OldTy = |
449 | OldDTy->getAs<ObjCObjectPointerType>(); |
450 | if (!OldTy) |
451 | return nullptr; |
452 | |
453 | // Id the old type is 'id', the new one is more precise. |
454 | if (OldTy->isObjCIdType() && !NewTy->isObjCIdType()) |
455 | return NewTy; |
456 | |
457 | // Return new if it's a subclass of old. |
458 | const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl(); |
459 | const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl(); |
460 | if (ToI && FromI && FromI->isSuperClassOf(I: ToI)) |
461 | return NewTy; |
462 | |
463 | return nullptr; |
464 | } |
465 | |
466 | static const ObjCObjectPointerType *getMostInformativeDerivedClassImpl( |
467 | const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, |
468 | const ObjCObjectPointerType *MostInformativeCandidate, ASTContext &C) { |
469 | // Checking if from and to are the same classes modulo specialization. |
470 | if (From->getInterfaceDecl()->getCanonicalDecl() == |
471 | To->getInterfaceDecl()->getCanonicalDecl()) { |
472 | if (To->isSpecialized()) { |
473 | assert(MostInformativeCandidate->isSpecialized()); |
474 | return MostInformativeCandidate; |
475 | } |
476 | return From; |
477 | } |
478 | |
479 | 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 | return From; |
484 | } |
485 | |
486 | const auto *SuperOfTo = |
487 | To->getObjectType()->getSuperClassType()->castAs<ObjCObjectType>(); |
488 | assert(SuperOfTo); |
489 | QualType SuperPtrOfToQual = |
490 | C.getObjCObjectPointerType(OIT: QualType(SuperOfTo, 0)); |
491 | const auto *SuperPtrOfTo = SuperPtrOfToQual->castAs<ObjCObjectPointerType>(); |
492 | if (To->isUnspecialized()) |
493 | return getMostInformativeDerivedClassImpl(From, To: SuperPtrOfTo, MostInformativeCandidate: SuperPtrOfTo, |
494 | C); |
495 | else |
496 | return getMostInformativeDerivedClassImpl(From, To: SuperPtrOfTo, |
497 | MostInformativeCandidate, C); |
498 | } |
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 | const ObjCObjectPointerType *To, ASTContext &C) { |
514 | return getMostInformativeDerivedClassImpl(From, To, MostInformativeCandidate: To, C); |
515 | } |
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 | 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 | assert(StaticUpperBound->isSpecialized() || |
562 | StaticLowerBound->isSpecialized()); |
563 | assert(!Current || (*Current)->isSpecialized()); |
564 | |
565 | // Case (1) |
566 | if (!Current) { |
567 | if (StaticUpperBound->isUnspecialized()) { |
568 | State = State->set<MostSpecializedTypeArgsMap>(K: Sym, E: StaticLowerBound); |
569 | return true; |
570 | } |
571 | // Upper bound is specialized. |
572 | const ObjCObjectPointerType *WithMostInfo = |
573 | getMostInformativeDerivedClass(From: StaticUpperBound, To: StaticLowerBound, C); |
574 | State = State->set<MostSpecializedTypeArgsMap>(K: Sym, E: WithMostInfo); |
575 | return true; |
576 | } |
577 | |
578 | // Case (3) |
579 | if (C.canAssignObjCInterfaces(LHSOPT: StaticLowerBound, RHSOPT: *Current)) { |
580 | return false; |
581 | } |
582 | |
583 | // Case (4) |
584 | if (C.canAssignObjCInterfaces(LHSOPT: *Current, RHSOPT: StaticUpperBound)) { |
585 | // The type arguments might not be forwarded at any point of inheritance. |
586 | const ObjCObjectPointerType *WithMostInfo = |
587 | getMostInformativeDerivedClass(From: *Current, To: StaticUpperBound, C); |
588 | WithMostInfo = |
589 | getMostInformativeDerivedClass(From: WithMostInfo, To: StaticLowerBound, C); |
590 | if (WithMostInfo == *Current) |
591 | return false; |
592 | State = State->set<MostSpecializedTypeArgsMap>(K: Sym, E: WithMostInfo); |
593 | return true; |
594 | } |
595 | |
596 | // Case (2) |
597 | const ObjCObjectPointerType *WithMostInfo = |
598 | getMostInformativeDerivedClass(From: *Current, To: StaticLowerBound, C); |
599 | if (WithMostInfo != *Current) { |
600 | State = State->set<MostSpecializedTypeArgsMap>(K: Sym, E: WithMostInfo); |
601 | return true; |
602 | } |
603 | |
604 | return false; |
605 | } |
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 | CheckerContext &C) const { |
612 | if (CE->getCastKind() != CK_BitCast) |
613 | return; |
614 | |
615 | QualType OriginType = CE->getSubExpr()->getType(); |
616 | QualType DestType = CE->getType(); |
617 | |
618 | const auto *OrigObjectPtrType = OriginType->getAs<ObjCObjectPointerType>(); |
619 | const auto *DestObjectPtrType = DestType->getAs<ObjCObjectPointerType>(); |
620 | |
621 | if (!OrigObjectPtrType || !DestObjectPtrType) |
622 | return; |
623 | |
624 | ProgramStateRef State = C.getState(); |
625 | ExplodedNode *AfterTypeProp = dynamicTypePropagationOnCasts(CE, State, C); |
626 | |
627 | 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 | OrigObjectPtrType = OrigObjectPtrType->stripObjCKindOfTypeAndQuals(ctx: ASTCtxt); |
635 | DestObjectPtrType = DestObjectPtrType->stripObjCKindOfTypeAndQuals(ctx: ASTCtxt); |
636 | |
637 | if (OrigObjectPtrType->isUnspecialized() && |
638 | DestObjectPtrType->isUnspecialized()) |
639 | return; |
640 | |
641 | SymbolRef Sym = C.getSVal(S: CE).getAsSymbol(); |
642 | if (!Sym) |
643 | return; |
644 | |
645 | const ObjCObjectPointerType *const *TrackedType = |
646 | State->get<MostSpecializedTypeArgsMap>(key: Sym); |
647 | |
648 | if (isa<ExplicitCastExpr>(Val: 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 | if (TrackedType) { |
658 | State = State->remove<MostSpecializedTypeArgsMap>(K: Sym); |
659 | C.addTransition(State, Pred: AfterTypeProp); |
660 | } |
661 | return; |
662 | } |
663 | |
664 | // Check which assignments are legal. |
665 | bool OrigToDest = |
666 | ASTCtxt.canAssignObjCInterfaces(LHSOPT: DestObjectPtrType, RHSOPT: OrigObjectPtrType); |
667 | bool DestToOrig = |
668 | ASTCtxt.canAssignObjCInterfaces(LHSOPT: OrigObjectPtrType, RHSOPT: 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 | if (TrackedType && |
675 | !ASTCtxt.canAssignObjCInterfaces(LHSOPT: DestObjectPtrType, RHSOPT: *TrackedType) && |
676 | !ASTCtxt.canAssignObjCInterfaces(LHSOPT: *TrackedType, RHSOPT: DestObjectPtrType)) { |
677 | static CheckerProgramPointTag IllegalConv(this, "IllegalConversion" ); |
678 | ExplodedNode *N = C.addTransition(State, Pred: AfterTypeProp, Tag: &IllegalConv); |
679 | reportGenericsBug(From: *TrackedType, To: DestObjectPtrType, N, Sym, C); |
680 | return; |
681 | } |
682 | |
683 | // Handle downcasts and upcasts. |
684 | |
685 | const ObjCObjectPointerType *LowerBound = DestObjectPtrType; |
686 | const ObjCObjectPointerType *UpperBound = OrigObjectPtrType; |
687 | if (OrigToDest && !DestToOrig) |
688 | std::swap(a&: LowerBound, b&: UpperBound); |
689 | |
690 | // The id type is not a real bound. Eliminate it. |
691 | LowerBound = LowerBound->isObjCIdType() ? UpperBound : LowerBound; |
692 | UpperBound = UpperBound->isObjCIdType() ? LowerBound : UpperBound; |
693 | |
694 | if (storeWhenMoreInformative(State, Sym, Current: TrackedType, StaticLowerBound: LowerBound, StaticUpperBound: UpperBound, |
695 | C&: ASTCtxt)) { |
696 | C.addTransition(State, Pred: AfterTypeProp); |
697 | } |
698 | } |
699 | |
700 | static const Expr *stripCastsAndSugar(const Expr *E) { |
701 | E = E->IgnoreParenImpCasts(); |
702 | if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(Val: E)) |
703 | E = POE->getSyntacticForm()->IgnoreParenImpCasts(); |
704 | if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Val: E)) |
705 | E = OVE->getSourceExpr()->IgnoreParenImpCasts(); |
706 | return E; |
707 | } |
708 | |
709 | 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 | class IsObjCTypeParamDependentTypeVisitor |
714 | : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { |
715 | public: |
716 | IsObjCTypeParamDependentTypeVisitor() = default; |
717 | bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { |
718 | if (isa<ObjCTypeParamDecl>(Val: Type->getDecl())) { |
719 | Result = true; |
720 | return false; |
721 | } |
722 | return true; |
723 | } |
724 | |
725 | bool Result = false; |
726 | }; |
727 | |
728 | IsObjCTypeParamDependentTypeVisitor Visitor; |
729 | Visitor.TraverseType(T: Type); |
730 | return Visitor.Result; |
731 | } |
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 | const ObjCObjectPointerType *TrackedType, ASTContext &ASTCtxt) { |
741 | const ObjCMethodDecl *Method = nullptr; |
742 | |
743 | 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 | if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Instance || |
748 | MessageExpr->getReceiverKind() == ObjCMessageExpr::Class) { |
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 | if (ReceiverType->isObjCIdType() || ReceiverType->isObjCClassType() || |
753 | ASTCtxt.canAssignObjCInterfaces( |
754 | LHSOPT: ReceiverType->castAs<ObjCObjectPointerType>(), RHSOPT: TrackedType)) { |
755 | const ObjCInterfaceDecl *InterfaceDecl = TrackedType->getInterfaceDecl(); |
756 | // The method might not be found. |
757 | Selector Sel = MessageExpr->getSelector(); |
758 | Method = InterfaceDecl->lookupInstanceMethod(Sel); |
759 | if (!Method) |
760 | Method = InterfaceDecl->lookupClassMethod(Sel); |
761 | } |
762 | } |
763 | |
764 | // Fallback to statick method lookup when the one based on the tracked type |
765 | // failed. |
766 | return Method ? Method : MessageExpr->getMethodDecl(); |
767 | } |
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 | const ObjCObjectPointerType *SelfType, ASTContext &C) { |
775 | QualType StaticResultType = Method->getReturnType(); |
776 | |
777 | // Is the return type declared as instance type? |
778 | if (StaticResultType == C.getObjCInstanceType()) |
779 | return QualType(SelfType, 0); |
780 | |
781 | // Check whether the result type depends on a type parameter. |
782 | if (!isObjCTypeParamDependent(Type: StaticResultType)) |
783 | return QualType(); |
784 | |
785 | QualType ResultType = StaticResultType.substObjCTypeArgs( |
786 | ctx&: C, typeArgs: TypeArgs, context: ObjCSubstitutionContext::Result); |
787 | |
788 | return ResultType; |
789 | } |
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 | CheckerContext &C) const { |
795 | ProgramStateRef State = C.getState(); |
796 | SymbolRef Sym = M.getReceiverSVal().getAsSymbol(); |
797 | if (!Sym) |
798 | return; |
799 | |
800 | const ObjCObjectPointerType *const *TrackedType = |
801 | State->get<MostSpecializedTypeArgsMap>(key: Sym); |
802 | if (!TrackedType) |
803 | return; |
804 | |
805 | // Get the type arguments from tracked type and substitute type arguments |
806 | // before do the semantic check. |
807 | |
808 | ASTContext &ASTCtxt = C.getASTContext(); |
809 | const ObjCMessageExpr *MessageExpr = M.getOriginExpr(); |
810 | const ObjCMethodDecl *Method = |
811 | findMethodDecl(MessageExpr, TrackedType: *TrackedType, ASTCtxt); |
812 | |
813 | // It is possible to call non-existent methods in Obj-C. |
814 | if (!Method) |
815 | 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 | const ObjCInterfaceDecl *Interface = Method->getClassInterface(); |
836 | if (!Interface) |
837 | return; |
838 | |
839 | ObjCTypeParamList *TypeParams = Interface->getTypeParamList(); |
840 | if (!TypeParams) |
841 | return; |
842 | |
843 | for (ObjCTypeParamDecl *TypeParam : *TypeParams) { |
844 | if (TypeParam->getVariance() != ObjCTypeParamVariance::Invariant) |
845 | return; |
846 | } |
847 | |
848 | std::optional<ArrayRef<QualType>> TypeArgs = |
849 | (*TrackedType)->getObjCSubstitutions(dc: Method->getDeclContext()); |
850 | // This case might happen when there is an unspecialized override of a |
851 | // specialized method. |
852 | if (!TypeArgs) |
853 | return; |
854 | |
855 | for (unsigned i = 0; i < Method->param_size(); i++) { |
856 | const Expr *Arg = MessageExpr->getArg(Arg: i); |
857 | const ParmVarDecl *Param = Method->parameters()[i]; |
858 | |
859 | QualType OrigParamType = Param->getType(); |
860 | if (!isObjCTypeParamDependent(Type: OrigParamType)) |
861 | continue; |
862 | |
863 | QualType ParamType = OrigParamType.substObjCTypeArgs( |
864 | ctx&: ASTCtxt, typeArgs: *TypeArgs, context: ObjCSubstitutionContext::Parameter); |
865 | // Check if it can be assigned |
866 | const auto *ParamObjectPtrType = ParamType->getAs<ObjCObjectPointerType>(); |
867 | const auto *ArgObjectPtrType = |
868 | stripCastsAndSugar(E: Arg)->getType()->getAs<ObjCObjectPointerType>(); |
869 | if (!ParamObjectPtrType || !ArgObjectPtrType) |
870 | continue; |
871 | |
872 | // Check if we have more concrete tracked type that is not a super type of |
873 | // the static argument type. |
874 | SVal ArgSVal = M.getArgSVal(Index: i); |
875 | SymbolRef ArgSym = ArgSVal.getAsSymbol(); |
876 | if (ArgSym) { |
877 | const ObjCObjectPointerType *const *TrackedArgType = |
878 | State->get<MostSpecializedTypeArgsMap>(key: ArgSym); |
879 | if (TrackedArgType && |
880 | ASTCtxt.canAssignObjCInterfaces(LHSOPT: ArgObjectPtrType, RHSOPT: *TrackedArgType)) { |
881 | ArgObjectPtrType = *TrackedArgType; |
882 | } |
883 | } |
884 | |
885 | // Warn when argument is incompatible with the parameter. |
886 | if (!ASTCtxt.canAssignObjCInterfaces(LHSOPT: ParamObjectPtrType, |
887 | RHSOPT: ArgObjectPtrType)) { |
888 | static CheckerProgramPointTag Tag(this, "ArgTypeMismatch" ); |
889 | ExplodedNode *N = C.addTransition(State, Tag: &Tag); |
890 | reportGenericsBug(From: ArgObjectPtrType, To: ParamObjectPtrType, N, Sym, C, ReportedNode: Arg); |
891 | return; |
892 | } |
893 | } |
894 | } |
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 | CheckerContext &C) const { |
905 | const ObjCMessageExpr *MessageExpr = M.getOriginExpr(); |
906 | |
907 | SymbolRef RetSym = M.getReturnValue().getAsSymbol(); |
908 | if (!RetSym) |
909 | return; |
910 | |
911 | Selector Sel = MessageExpr->getSelector(); |
912 | ProgramStateRef State = C.getState(); |
913 | |
914 | // Here we try to propagate information on Class objects. |
915 | if (Sel.getAsString() == "class" ) { |
916 | // We try to figure out the type from the receiver of the 'class' message. |
917 | if (RuntimeType ReceiverRuntimeType = inferReceiverType(Message: M, C)) { |
918 | |
919 | ReceiverRuntimeType.Type->getSuperClassType(); |
920 | QualType ReceiverClassType(ReceiverRuntimeType.Type, 0); |
921 | |
922 | // We want to consider only precise information on generics. |
923 | if (ReceiverRuntimeType.Type->isSpecialized() && |
924 | ReceiverRuntimeType.Precise) { |
925 | QualType ReceiverClassPointerType = |
926 | C.getASTContext().getObjCObjectPointerType(OIT: ReceiverClassType); |
927 | const auto *InferredType = |
928 | ReceiverClassPointerType->castAs<ObjCObjectPointerType>(); |
929 | State = State->set<MostSpecializedTypeArgsMap>(K: RetSym, E: InferredType); |
930 | } |
931 | |
932 | // Constrain the resulting class object to the inferred type. |
933 | State = setClassObjectDynamicTypeInfo(State, Sym: RetSym, NewTy: ReceiverClassType, |
934 | CanBeSubClassed: !ReceiverRuntimeType.Precise); |
935 | |
936 | C.addTransition(State); |
937 | return; |
938 | } |
939 | } |
940 | |
941 | if (Sel.getAsString() == "superclass" ) { |
942 | // We try to figure out the type from the receiver of the 'superclass' |
943 | // message. |
944 | if (RuntimeType ReceiverRuntimeType = inferReceiverType(Message: M, C)) { |
945 | |
946 | // Result type would be a super class of the receiver's type. |
947 | QualType = |
948 | 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 | if (!ReceiversSuperClass.isNull()) { |
955 | // Constrain the resulting class object to the inferred type. |
956 | State = setClassObjectDynamicTypeInfo( |
957 | State, Sym: RetSym, NewTy: ReceiversSuperClass, CanBeSubClassed: !ReceiverRuntimeType.Precise); |
958 | |
959 | C.addTransition(State); |
960 | } |
961 | return; |
962 | } |
963 | } |
964 | |
965 | // Tracking for return types. |
966 | SymbolRef RecSym = M.getReceiverSVal().getAsSymbol(); |
967 | if (!RecSym) |
968 | return; |
969 | |
970 | const ObjCObjectPointerType *const *TrackedType = |
971 | State->get<MostSpecializedTypeArgsMap>(key: RecSym); |
972 | if (!TrackedType) |
973 | return; |
974 | |
975 | ASTContext &ASTCtxt = C.getASTContext(); |
976 | const ObjCMethodDecl *Method = |
977 | findMethodDecl(MessageExpr, TrackedType: *TrackedType, ASTCtxt); |
978 | if (!Method) |
979 | return; |
980 | |
981 | std::optional<ArrayRef<QualType>> TypeArgs = |
982 | (*TrackedType)->getObjCSubstitutions(dc: Method->getDeclContext()); |
983 | if (!TypeArgs) |
984 | return; |
985 | |
986 | QualType ResultType = |
987 | getReturnTypeForMethod(Method, TypeArgs: *TypeArgs, SelfType: *TrackedType, C&: ASTCtxt); |
988 | // The static type is the same as the deduced type. |
989 | if (ResultType.isNull()) |
990 | return; |
991 | |
992 | const MemRegion *RetRegion = M.getReturnValue().getAsRegion(); |
993 | 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 | if (RetRegion && !getRawDynamicTypeInfo(State, MR: 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 | State = setDynamicTypeInfo(State, MR: RetRegion, NewTy: ResultType, |
1002 | /*CanBeSubClassed=*/true); |
1003 | Pred = C.addTransition(State); |
1004 | } |
1005 | |
1006 | const auto *ResultPtrType = ResultType->getAs<ObjCObjectPointerType>(); |
1007 | |
1008 | if (!ResultPtrType || ResultPtrType->isUnspecialized()) |
1009 | return; |
1010 | |
1011 | // When the result is a specialized type and it is not tracked yet, track it |
1012 | // for the result symbol. |
1013 | if (!State->get<MostSpecializedTypeArgsMap>(key: RetSym)) { |
1014 | State = State->set<MostSpecializedTypeArgsMap>(K: RetSym, E: ResultPtrType); |
1015 | C.addTransition(State, Pred); |
1016 | } |
1017 | } |
1018 | |
1019 | void DynamicTypePropagation::reportGenericsBug( |
1020 | const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, |
1021 | ExplodedNode *N, SymbolRef Sym, CheckerContext &C, |
1022 | const Stmt *ReportedNode) const { |
1023 | if (!CheckGenerics) |
1024 | return; |
1025 | |
1026 | initBugType(); |
1027 | SmallString<192> Buf; |
1028 | llvm::raw_svector_ostream OS(Buf); |
1029 | OS << "Conversion from value of type '" ; |
1030 | QualType::print(ty: From, qs: Qualifiers(), OS, policy: C.getLangOpts(), PlaceHolder: llvm::Twine()); |
1031 | OS << "' to incompatible type '" ; |
1032 | QualType::print(ty: To, qs: Qualifiers(), OS, policy: C.getLangOpts(), PlaceHolder: llvm::Twine()); |
1033 | OS << "'" ; |
1034 | auto R = std::make_unique<PathSensitiveBugReport>(args&: *ObjCGenericsBugType, |
1035 | args: OS.str(), args&: N); |
1036 | R->markInteresting(sym: Sym); |
1037 | R->addVisitor(visitor: std::make_unique<GenericsBugVisitor>(args&: Sym)); |
1038 | if (ReportedNode) |
1039 | R->addRange(R: ReportedNode->getSourceRange()); |
1040 | C.emitReport(R: std::move(R)); |
1041 | } |
1042 | |
1043 | PathDiagnosticPieceRef DynamicTypePropagation::GenericsBugVisitor::VisitNode( |
1044 | const ExplodedNode *N, BugReporterContext &BRC, |
1045 | PathSensitiveBugReport &BR) { |
1046 | ProgramStateRef state = N->getState(); |
1047 | ProgramStateRef statePrev = N->getFirstPred()->getState(); |
1048 | |
1049 | const ObjCObjectPointerType *const *TrackedType = |
1050 | state->get<MostSpecializedTypeArgsMap>(key: Sym); |
1051 | const ObjCObjectPointerType *const *TrackedTypePrev = |
1052 | statePrev->get<MostSpecializedTypeArgsMap>(key: Sym); |
1053 | if (!TrackedType) |
1054 | return nullptr; |
1055 | |
1056 | if (TrackedTypePrev && *TrackedTypePrev == *TrackedType) |
1057 | return nullptr; |
1058 | |
1059 | // Retrieve the associated statement. |
1060 | const Stmt *S = N->getStmtForDiagnostics(); |
1061 | if (!S) |
1062 | return nullptr; |
1063 | |
1064 | const LangOptions &LangOpts = BRC.getASTContext().getLangOpts(); |
1065 | |
1066 | SmallString<256> Buf; |
1067 | llvm::raw_svector_ostream OS(Buf); |
1068 | OS << "Type '" ; |
1069 | QualType::print(ty: *TrackedType, qs: Qualifiers(), OS, policy: LangOpts, PlaceHolder: llvm::Twine()); |
1070 | OS << "' is inferred from " ; |
1071 | |
1072 | if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(Val: S)) { |
1073 | OS << "explicit cast (from '" ; |
1074 | QualType::print(ty: ExplicitCast->getSubExpr()->getType().getTypePtr(), |
1075 | qs: Qualifiers(), OS, policy: LangOpts, PlaceHolder: llvm::Twine()); |
1076 | OS << "' to '" ; |
1077 | QualType::print(ty: ExplicitCast->getType().getTypePtr(), qs: Qualifiers(), OS, |
1078 | policy: LangOpts, PlaceHolder: llvm::Twine()); |
1079 | OS << "')" ; |
1080 | } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(Val: S)) { |
1081 | OS << "implicit cast (from '" ; |
1082 | QualType::print(ty: ImplicitCast->getSubExpr()->getType().getTypePtr(), |
1083 | qs: Qualifiers(), OS, policy: LangOpts, PlaceHolder: llvm::Twine()); |
1084 | OS << "' to '" ; |
1085 | QualType::print(ty: ImplicitCast->getType().getTypePtr(), qs: Qualifiers(), OS, |
1086 | policy: LangOpts, PlaceHolder: llvm::Twine()); |
1087 | OS << "')" ; |
1088 | } else { |
1089 | OS << "this context" ; |
1090 | } |
1091 | |
1092 | // Generate the extra diagnostic. |
1093 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
1094 | N->getLocationContext()); |
1095 | return std::make_shared<PathDiagnosticEventPiece>(args&: Pos, args: OS.str(), args: true); |
1096 | } |
1097 | |
1098 | /// Register checkers. |
1099 | void ento::registerObjCGenericsChecker(CheckerManager &mgr) { |
1100 | DynamicTypePropagation *checker = mgr.getChecker<DynamicTypePropagation>(); |
1101 | checker->CheckGenerics = true; |
1102 | checker->GenericCheckName = mgr.getCurrentCheckerName(); |
1103 | } |
1104 | |
1105 | bool ento::shouldRegisterObjCGenericsChecker(const CheckerManager &mgr) { |
1106 | return true; |
1107 | } |
1108 | |
1109 | void ento::registerDynamicTypePropagation(CheckerManager &mgr) { |
1110 | mgr.registerChecker<DynamicTypePropagation>(); |
1111 | } |
1112 | |
1113 | bool ento::shouldRegisterDynamicTypePropagation(const CheckerManager &mgr) { |
1114 | return true; |
1115 | } |
1116 | |