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