[Static Analyzer] General type checker based on dynamic type information.

Differential Revision: http://reviews.llvm.org/D12973

llvm-svn: 248041
This commit is contained in:
Gabor Horvath 2015-09-18 21:15:37 +00:00
parent 34b9ef680f
commit dce40c518d
6 changed files with 697 additions and 332 deletions

View File

@ -33,6 +33,7 @@ add_clang_library(clangStaticAnalyzerCheckers
DirectIvarAssignment.cpp
DivZeroChecker.cpp
DynamicTypePropagation.cpp
DynamicTypeChecker.cpp
ExprInspectionChecker.cpp
FixedAddressChecker.cpp
GenericTaintChecker.cpp

View File

@ -129,6 +129,10 @@ def TestAfterDivZeroChecker : Checker<"TestAfterDivZero">,
HelpText<"Check for division by variable that is later compared against 0. Either the comparison is useless or there is division by zero.">,
DescFile<"TestAfterDivZeroChecker.cpp">;
def DynamicTypeChecker : Checker<"DynamicTypeChecker">,
HelpText<"Check for cases where the dynamic and the static type of an object are unrelated.">,
DescFile<"DynamicTypeChecker.cpp">;
} // end "alpha.core"
let ParentPackage = Nullability in {

View File

@ -0,0 +1,202 @@
//== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This checker looks for cases where the dynamic type of an object is unrelated
// to its static type. The type information utilized by this check is collected
// by the DynamicTypePropagation checker. This check does not report any type
// error for ObjC Generic types, in order to avoid duplicate erros from the
// ObjC Generics checker. This checker is not supposed to modify the program
// state, it is just the observer of the type information provided by other
// checkers.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
using namespace clang;
using namespace ento;
namespace {
class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
mutable std::unique_ptr<BugType> BT;
void initBugType() const {
if (!BT)
BT.reset(
new BugType(this, "Dynamic and static type mismatch", "Type Error"));
}
class DynamicTypeBugVisitor
: public BugReporterVisitorImpl<DynamicTypeBugVisitor> {
public:
DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int X = 0;
ID.AddPointer(&X);
ID.AddPointer(Reg);
}
PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
BugReport &BR) override;
private:
// The tracked region.
const MemRegion *Reg;
};
void reportTypeError(QualType DynamicType, QualType StaticType,
const MemRegion *Reg, const Stmt *ReportedNode,
CheckerContext &C) const;
public:
void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
};
}
void DynamicTypeChecker::reportTypeError(QualType DynamicType,
QualType StaticType,
const MemRegion *Reg,
const Stmt *ReportedNode,
CheckerContext &C) const {
initBugType();
SmallString<192> Buf;
llvm::raw_svector_ostream OS(Buf);
OS << "Object has a dynamic type '";
QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
llvm::Twine());
OS << "' which is incompatible with static type '";
QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
llvm::Twine());
OS << "'";
std::unique_ptr<BugReport> R(
new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode()));
R->markInteresting(Reg);
R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg));
R->addRange(ReportedNode->getSourceRange());
C.emitReport(std::move(R));
}
PathDiagnosticPiece *DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC,
BugReport &BR) {
ProgramStateRef State = N->getState();
ProgramStateRef StatePrev = PrevN->getState();
DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
if (!TrackedType.isValid())
return nullptr;
if (TrackedTypePrev.isValid() &&
TrackedTypePrev.getType() == TrackedType.getType())
return nullptr;
// Retrieve the associated statement.
const Stmt *S = nullptr;
ProgramPoint ProgLoc = N->getLocation();
if (Optional<StmtPoint> SP = ProgLoc.getAs<StmtPoint>()) {
S = SP->getStmt();
}
if (!S)
return nullptr;
const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
SmallString<256> Buf;
llvm::raw_svector_ostream OS(Buf);
OS << "Type '";
QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
LangOpts, llvm::Twine());
OS << "' is inferred from ";
if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
OS << "explicit cast (from '";
QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
Qualifiers(), OS, LangOpts, llvm::Twine());
OS << "' to '";
QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
LangOpts, llvm::Twine());
OS << "')";
} else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
OS << "implicit cast (from '";
QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
Qualifiers(), OS, LangOpts, llvm::Twine());
OS << "' to '";
QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
LangOpts, llvm::Twine());
OS << "')";
} else {
OS << "this context";
}
// Generate the extra diagnostic.
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
return new PathDiagnosticEventPiece(Pos, OS.str(), true, nullptr);
}
// TODO: consider checking explicit casts?
void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
CheckerContext &C) const {
// TODO: C++ support.
if (CE->getCastKind() != CK_BitCast)
return;
const MemRegion *Region = C.getSVal(CE).getAsRegion();
if (!Region)
return;
ProgramStateRef State = C.getState();
DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
if (!DynTypeInfo.isValid())
return;
QualType DynType = DynTypeInfo.getType();
QualType StaticType = CE->getType();
const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
if (!DynObjCType || !StaticObjCType)
return;
ASTContext &ASTCtxt = C.getASTContext();
// Strip kindeofness to correctly detect subtyping relationships.
DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
// Specialized objects are handled by the generics checker.
if (StaticObjCType->isSpecialized())
return;
if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
return;
if (DynTypeInfo.canBeASubClass() &&
ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
return;
reportTypeError(DynType, StaticType, Region, CE, C);
}
void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
mgr.registerChecker<DynamicTypeChecker>();
}

View File

@ -22,7 +22,6 @@
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@ -98,13 +97,6 @@ class DynamicTypePropagation:
const ObjCObjectPointerType *To, ExplodedNode *N,
SymbolRef Sym, CheckerContext &C,
const Stmt *ReportedNode = nullptr) const;
bool isReturnValueMisused(const ObjCMessageExpr *MessageExpr,
const ObjCObjectPointerType *TrackedType,
SymbolRef Sym, const ObjCMethodDecl *Method,
ArrayRef<QualType> TypeArgs,
bool SubscriptOrProperty, CheckerContext &C) const;
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@ -684,46 +676,6 @@ static QualType getReturnTypeForMethod(
return ResultType;
}
/// Validate that the return type of a message expression is used correctly.
/// Returns true in case an error is detected.
bool DynamicTypePropagation::isReturnValueMisused(
const ObjCMessageExpr *MessageExpr,
const ObjCObjectPointerType *ResultPtrType, SymbolRef Sym,
const ObjCMethodDecl *Method, ArrayRef<QualType> TypeArgs,
bool SubscriptOrProperty, CheckerContext &C) const {
if (!ResultPtrType)
return false;
ASTContext &ASTCtxt = C.getASTContext();
const Stmt *Parent =
C.getCurrentAnalysisDeclContext()->getParentMap().getParent(MessageExpr);
if (SubscriptOrProperty) {
// Properties and subscripts are not direct parents.
Parent =
C.getCurrentAnalysisDeclContext()->getParentMap().getParent(Parent);
}
const auto *ImplicitCast = dyn_cast_or_null<ImplicitCastExpr>(Parent);
if (!ImplicitCast || ImplicitCast->getCastKind() != CK_BitCast)
return false;
const auto *ExprTypeAboveCast =
ImplicitCast->getType()->getAs<ObjCObjectPointerType>();
if (!ExprTypeAboveCast)
return false;
// Only warn on unrelated types to avoid too many false positives on
// downcasts.
if (!ASTCtxt.canAssignObjCInterfaces(ExprTypeAboveCast, ResultPtrType) &&
!ASTCtxt.canAssignObjCInterfaces(ResultPtrType, ExprTypeAboveCast)) {
static CheckerProgramPointTag Tag(this, "ReturnTypeMismatch");
ExplodedNode *N = C.addTransition(C.getState(), &Tag);
reportGenericsBug(ResultPtrType, ExprTypeAboveCast, N, Sym, C);
return true;
}
return false;
}
/// When the receiver has a tracked type, use that type to validate the
/// argumments of the message expression and the return value.
void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M,
@ -881,10 +833,6 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M,
const auto *ResultPtrType = ResultType->getAs<ObjCObjectPointerType>();
if (isReturnValueMisused(MessageExpr, ResultPtrType, RecSym, Method,
*TypeArgs, M.getMessageKind() != OCM_Message, C))
return;
if (!ResultPtrType || ResultPtrType->isUnspecialized())
return;

View File

@ -0,0 +1,33 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core.DynamicTypeChecker -verify %s
#define nil 0
typedef unsigned long NSUInteger;
typedef int BOOL;
@protocol NSObject
+ (id)alloc;
- (id)init;
@end
@protocol NSCopying
@end
__attribute__((objc_root_class))
@interface NSObject <NSObject>
@end
@interface NSString : NSObject <NSCopying>
@end
@interface NSMutableString : NSString
@end
@interface NSNumber : NSObject <NSCopying>
@end
void testTypeCheck(NSString* str) {
id obj = str;
NSNumber *num = obj; // expected-warning {{}}
(void)num;
}

View File

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.osx.cocoa.ObjCGenerics -verify -Wno-objc-method-access %s
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.osx.cocoa.ObjCGenerics -verify -Wno-objc-method-access %s -analyzer-output=plist -o %t.plist
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.osx.cocoa.ObjCGenerics,alpha.core.DynamicTypeChecker -verify -Wno-objc-method-access %s
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.osx.cocoa.ObjCGenerics,alpha.core.DynamicTypeChecker -verify -Wno-objc-method-access %s -analyzer-output=plist -o %t.plist
// RUN: FileCheck --input-file %t.plist %s
#if !__has_feature(objc_generics)
@ -236,13 +236,13 @@ void enforceDynamicRulesInsteadOfStatic(NSArray<NSNumber *> *a) {
void workWithProperties(NSArray<NSNumber *> *a) {
NSArray *b = a;
NSString *str = [b getObjAtIndex: 0]; // expected-warning {{Conversion}}
NSString *str = [b getObjAtIndex: 0]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}}
NSNumber *num = [b getObjAtIndex: 0];
str = [b firstObject]; // expected-warning {{Conversion}}
str = [b firstObject]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}}
num = [b firstObject];
str = b.firstObject; // expected-warning {{Conversion}}
str = b.firstObject; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}}
num = b.firstObject;
str = b[0]; // expected-warning {{Conversion}}
str = b[0]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}}
num = b[0];
}
@ -318,15 +318,14 @@ void eraseSpecialization(NSArray<NSArray<NSString *> *> *arr) {
void returnToUnrelatedType(NSArray<NSArray<NSString *> *> *arr) {
NSArray *erased = arr;
NSSet* a = [erased firstObject]; // expected-warning {{Conversion}}
NSSet* a = [erased firstObject]; // expected-warning {{Object has a dynamic type 'NSArray<NSString *> *' which is incompatible with static type 'NSSet *'}}
(void)a;
}
void returnToIdVariable(NSArray<NSString *> *arr) {
NSArray *erased = arr;
id a = [erased firstObject];
// TODO: Warn in this case. Possibly in a separate checker.
NSNumber *res = a;
NSNumber *res = a; // expected-warning {{Object has a dynamic type 'NSString *' which is incompatible with static type 'NSNumber *'}}
}
// CHECK: <array>
@ -4428,35 +4427,6 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSNumber *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSNumber *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: <key>message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSNumber *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSNumber *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@ -4549,15 +4519,44 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <string>Type &apos;NSNumber *&apos; is inferred from this context</string>
// CHECK: <key>message</key>
// CHECK: <string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <string>Type &apos;NSNumber *&apos; is inferred from this context</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>239</integer>
// CHECK: <key>col</key><integer>19</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>239</integer>
// CHECK: <key>col</key><integer>19</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>239</integer>
// CHECK: <key>col</key><integer>38</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: <key>message</key>
// CHECK: <string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <key>category</key><string>Core Foundation/Objective-C</string>
// CHECK: <key>type</key><string>Generics</string>
// CHECK: <key>check_name</key><string>core.DynamicTypePropagation</string>
// CHECK: <key>description</key><string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: <key>category</key><string>Type Error</string>
// CHECK: <key>type</key><string>Dynamic and static type mismatch</string>
// CHECK: <key>check_name</key><string>alpha.core.DynamicTypeChecker</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>workWithProperties</string>
// CHECK: <key>issue_hash</key><string>2</string>
@ -4572,35 +4571,6 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSNumber *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSNumber *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: <key>message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSNumber *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSNumber *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@ -4693,15 +4663,44 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <string>Type &apos;NSNumber *&apos; is inferred from this context</string>
// CHECK: <key>message</key>
// CHECK: <string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <string>Type &apos;NSNumber *&apos; is inferred from this context</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>241</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>241</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>241</integer>
// CHECK: <key>col</key><integer>23</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: <key>message</key>
// CHECK: <string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <key>category</key><string>Core Foundation/Objective-C</string>
// CHECK: <key>type</key><string>Generics</string>
// CHECK: <key>check_name</key><string>core.DynamicTypePropagation</string>
// CHECK: <key>description</key><string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: <key>category</key><string>Type Error</string>
// CHECK: <key>type</key><string>Dynamic and static type mismatch</string>
// CHECK: <key>check_name</key><string>alpha.core.DynamicTypeChecker</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>workWithProperties</string>
// CHECK: <key>issue_hash</key><string>4</string>
@ -4716,35 +4715,6 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSNumber *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSNumber *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: <key>message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSNumber *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSNumber *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@ -4837,57 +4807,57 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <string>Type &apos;NSNumber *&apos; is inferred from this context</string>
// CHECK: <key>message</key>
// CHECK: <string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <string>Type &apos;NSNumber *&apos; is inferred from this context</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <key>category</key><string>Core Foundation/Objective-C</string>
// CHECK: <key>type</key><string>Generics</string>
// CHECK: <key>check_name</key><string>core.DynamicTypePropagation</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>workWithProperties</string>
// CHECK: <key>issue_hash</key><string>6</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>243</integer>
// CHECK: <key>col</key><integer>11</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>line</key><integer>243</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>line</key><integer>243</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>238</integer>
// CHECK: <key>col</key><integer>16</integer>
// CHECK: <key>line</key><integer>243</integer>
// CHECK: <key>col</key><integer>21</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSNumber *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSNumber *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: <string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: <key>message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSNumber *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSNumber *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: <string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: <key>category</key><string>Type Error</string>
// CHECK: <key>type</key><string>Dynamic and static type mismatch</string>
// CHECK: <key>check_name</key><string>alpha.core.DynamicTypeChecker</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>workWithProperties</string>
// CHECK: <key>issue_hash</key><string>6</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>243</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
@ -4981,15 +4951,44 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <string>Type &apos;NSNumber *&apos; is inferred from this context</string>
// CHECK: <key>message</key>
// CHECK: <string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <string>Type &apos;NSNumber *&apos; is inferred from this context</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>245</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>245</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>245</integer>
// CHECK: <key>col</key><integer>12</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: <key>message</key>
// CHECK: <string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Conversion from value of type &apos;NSNumber *&apos; to incompatible type &apos;NSString *&apos;</string>
// CHECK: <key>category</key><string>Core Foundation/Objective-C</string>
// CHECK: <key>type</key><string>Generics</string>
// CHECK: <key>check_name</key><string>core.DynamicTypePropagation</string>
// CHECK: <key>description</key><string>Object has a dynamic type &apos;NSNumber *&apos; which is incompatible with static type &apos;NSString *&apos;</string>
// CHECK: <key>category</key><string>Type Error</string>
// CHECK: <key>type</key><string>Dynamic and static type mismatch</string>
// CHECK: <key>check_name</key><string>alpha.core.DynamicTypeChecker</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>workWithProperties</string>
// CHECK: <key>issue_hash</key><string>8</string>
@ -5436,35 +5435,6 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>13</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>13</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>39</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSString *&gt; *&apos; is inferred from this context</string>
// CHECK: <key>message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSString *&gt; *&apos; is inferred from this context</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@ -5557,6 +5527,35 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSString *&gt; *&apos; is inferred from this context</string>
// CHECK: <key>message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSString *&gt; *&apos; is inferred from this context</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>290</integer>
// CHECK: <key>col</key><integer>28</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>290</integer>
// CHECK: <key>col</key><integer>28</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>290</integer>
// CHECK: <key>col</key><integer>39</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Conversion from value of type &apos;NSArray&lt;NSString *&gt; *&apos; to incompatible type &apos;NSArray&lt;NSNumber *&gt; *&apos;</string>
// CHECK: <key>message</key>
// CHECK: <string>Conversion from value of type &apos;NSArray&lt;NSString *&gt; *&apos; to incompatible type &apos;NSArray&lt;NSNumber *&gt; *&apos;</string>
@ -5580,24 +5579,92 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>13</integer>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>13</integer>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>39</integer>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>19</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@ -5609,74 +5676,6 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: <string>Type &apos;NSArray&lt;NSString *&gt; *&apos; is inferred from this context</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>289</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>291</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
@ -6224,35 +6223,6 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>320</integer>
// CHECK: <key>col</key><integer>21</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>320</integer>
// CHECK: <key>col</key><integer>21</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>320</integer>
// CHECK: <key>col</key><integer>23</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSArray&lt;NSString *&gt; *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSArray&lt;NSString *&gt; *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: <key>message</key>
// CHECK: <string>Type &apos;NSArray&lt;NSArray&lt;NSString *&gt; *&gt; *&apos; is inferred from implicit cast (from &apos;NSArray&lt;NSArray&lt;NSString *&gt; *&gt; *&apos; to &apos;NSArray *&apos;)</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@ -6345,15 +6315,44 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Conversion from value of type &apos;NSArray&lt;NSString *&gt; *&apos; to incompatible type &apos;NSSet *&apos;</string>
// CHECK: <string>Type &apos;NSArray&lt;NSString *&gt; *&apos; is inferred from this context</string>
// CHECK: <key>message</key>
// CHECK: <string>Conversion from value of type &apos;NSArray&lt;NSString *&gt; *&apos; to incompatible type &apos;NSSet *&apos;</string>
// CHECK: <string>Type &apos;NSArray&lt;NSString *&gt; *&apos; is inferred from this context</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>321</integer>
// CHECK: <key>col</key><integer>14</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>321</integer>
// CHECK: <key>col</key><integer>14</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>321</integer>
// CHECK: <key>col</key><integer>33</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Object has a dynamic type &apos;NSArray&lt;NSString *&gt; *&apos; which is incompatible with static type &apos;NSSet *&apos;</string>
// CHECK: <key>message</key>
// CHECK: <string>Object has a dynamic type &apos;NSArray&lt;NSString *&gt; *&apos; which is incompatible with static type &apos;NSSet *&apos;</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Conversion from value of type &apos;NSArray&lt;NSString *&gt; *&apos; to incompatible type &apos;NSSet *&apos;</string>
// CHECK: <key>category</key><string>Core Foundation/Objective-C</string>
// CHECK: <key>type</key><string>Generics</string>
// CHECK: <key>check_name</key><string>core.DynamicTypePropagation</string>
// CHECK: <key>description</key><string>Object has a dynamic type &apos;NSArray&lt;NSString *&gt; *&apos; which is incompatible with static type &apos;NSSet *&apos;</string>
// CHECK: <key>category</key><string>Type Error</string>
// CHECK: <key>type</key><string>Dynamic and static type mismatch</string>
// CHECK: <key>check_name</key><string>alpha.core.DynamicTypeChecker</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>returnToUnrelatedType</string>
// CHECK: <key>issue_hash</key><string>2</string>
@ -6364,4 +6363,182 @@ void returnToIdVariable(NSArray<NSString *> *arr) {
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>326</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>326</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>327</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>327</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>327</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>327</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>327</integer>
// CHECK: <key>col</key><integer>29</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Type &apos;NSString *&apos; is inferred from this context</string>
// CHECK: <key>message</key>
// CHECK: <string>Type &apos;NSString *&apos; is inferred from this context</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>327</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>327</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>19</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>19</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>19</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>19</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>19</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Object has a dynamic type &apos;NSString *&apos; which is incompatible with static type &apos;NSNumber *&apos;</string>
// CHECK: <key>message</key>
// CHECK: <string>Object has a dynamic type &apos;NSString *&apos; which is incompatible with static type &apos;NSNumber *&apos;</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Object has a dynamic type &apos;NSString *&apos; which is incompatible with static type &apos;NSNumber *&apos;</string>
// CHECK: <key>category</key><string>Type Error</string>
// CHECK: <key>type</key><string>Dynamic and static type mismatch</string>
// CHECK: <key>check_name</key><string>alpha.core.DynamicTypeChecker</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>returnToIdVariable</string>
// CHECK: <key>issue_hash</key><string>3</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>328</integer>
// CHECK: <key>col</key><integer>19</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
// CHECK: </array>