--- /dev/null
+//== 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>();
+}
-// 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)
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];
}
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>
// 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 'NSArray<NSNumber *> *' is inferred from implicit cast (from 'NSArray<NSNumber *> *' to 'NSArray *')</string>
-// CHECK: <key>message</key>
-// CHECK: <string>Type 'NSArray<NSNumber *> *' is inferred from implicit cast (from 'NSArray<NSNumber *> *' to 'NSArray *')</string>
-// CHECK: </dict>
-// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</string>
+// CHECK: <string>Type 'NSNumber *' is inferred from this context</string>
// CHECK: <key>message</key>
-// CHECK: <string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</string>
+// CHECK: <string>Type 'NSNumber *' is inferred from this context</string>
// CHECK: </dict>
-// CHECK: </array>
-// CHECK: <key>description</key><string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</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>2</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: </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>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>238</integer>
-// CHECK: <key>col</key><integer>16</integer>
+// 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>238</integer>
-// CHECK: <key>col</key><integer>16</integer>
+// 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>Type 'NSArray<NSNumber *> *' is inferred from implicit cast (from 'NSArray<NSNumber *> *' to 'NSArray *')</string>
+// CHECK: <string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</string>
// CHECK: <key>message</key>
-// CHECK: <string>Type 'NSArray<NSNumber *> *' is inferred from implicit cast (from 'NSArray<NSNumber *> *' to 'NSArray *')</string>
+// CHECK: <string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</string>
// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</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>
+// 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: </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: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</string>
+// CHECK: <string>Type 'NSNumber *' is inferred from this context</string>
// CHECK: <key>message</key>
-// CHECK: <string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</string>
+// CHECK: <string>Type 'NSNumber *' is inferred from this context</string>
// CHECK: </dict>
-// CHECK: </array>
-// CHECK: <key>description</key><string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</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>4</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: </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>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>238</integer>
-// CHECK: <key>col</key><integer>16</integer>
+// 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>238</integer>
-// CHECK: <key>col</key><integer>16</integer>
+// 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>Type 'NSArray<NSNumber *> *' is inferred from implicit cast (from 'NSArray<NSNumber *> *' to 'NSArray *')</string>
+// CHECK: <string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</string>
// CHECK: <key>message</key>
-// CHECK: <string>Type 'NSArray<NSNumber *> *' is inferred from implicit cast (from 'NSArray<NSNumber *> *' to 'NSArray *')</string>
+// CHECK: <string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</string>
// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</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>
+// 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: </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: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</string>
+// CHECK: <string>Type 'NSNumber *' is inferred from this context</string>
// CHECK: <key>message</key>
-// CHECK: <string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</string>
+// CHECK: <string>Type 'NSNumber *' is inferred from this context</string>
// CHECK: </dict>
-// CHECK: </array>
-// CHECK: <key>description</key><string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</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 'NSArray<NSNumber *> *' is inferred from implicit cast (from 'NSArray<NSNumber *> *' to 'NSArray *')</string>
+// CHECK: <string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</string>
// CHECK: <key>message</key>
-// CHECK: <string>Type 'NSArray<NSNumber *> *' is inferred from implicit cast (from 'NSArray<NSNumber *> *' to 'NSArray *')</string>
+// CHECK: <string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</string>
// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</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>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</string>
+// CHECK: <string>Type 'NSNumber *' is inferred from this context</string>
// CHECK: <key>message</key>
-// CHECK: <string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</string>
+// CHECK: <string>Type 'NSNumber *' 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 'NSNumber *' which is incompatible with static type 'NSString *'</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'</string>
// CHECK: </dict>
// CHECK: </array>
-// CHECK: <key>description</key><string>Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'</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 'NSNumber *' which is incompatible with static type 'NSString *'</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>
// 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 'NSArray<NSString *> *' is inferred from this context</string>
-// CHECK: <key>message</key>
-// CHECK: <string>Type 'NSArray<NSString *> *' is inferred from this context</string>
-// CHECK: </dict>
-// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSArray<NSNumber *> *'</string>
+// CHECK: <string>Type 'NSArray<NSString *> *' is inferred from this context</string>
// CHECK: <key>message</key>
-// CHECK: <string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSArray<NSNumber *> *'</string>
+// CHECK: <string>Type 'NSArray<NSString *> *' is inferred from this context</string>
// CHECK: </dict>
-// CHECK: </array>
-// CHECK: <key>description</key><string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSArray<NSNumber *> *'</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>trackedClassVariables</string>
-// CHECK: <key>issue_hash</key><string>2</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: </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>289</integer>
-// CHECK: <key>col</key><integer>13</integer>
+// 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>289</integer>
-// CHECK: <key>col</key><integer>13</integer>
+// 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>289</integer>
+// 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: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Type 'NSArray<NSString *> *' is inferred from this context</string>
+// CHECK: <string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSArray<NSNumber *> *'</string>
// CHECK: <key>message</key>
-// CHECK: <string>Type 'NSArray<NSString *> *' is inferred from this context</string>
+// CHECK: <string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSArray<NSNumber *> *'</string>
// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSArray<NSNumber *> *'</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>trackedClassVariables</string>
+// CHECK: <key>issue_hash</key><string>2</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: </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: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
+// CHECK: <string>Type 'NSArray<NSString *> *' is inferred from this context</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Type 'NSArray<NSString *> *' 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>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>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>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>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSArray<NSNumber *> *'</string>
// CHECK: <key>message</key>
// CHECK: <string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSArray<NSNumber *> *'</string>
// 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 'NSArray<NSArray<NSString *> *> *' is inferred from implicit cast (from 'NSArray<NSArray<NSString *> *> *' to 'NSArray *')</string>
-// CHECK: <key>message</key>
-// CHECK: <string>Type 'NSArray<NSArray<NSString *> *> *' is inferred from implicit cast (from 'NSArray<NSArray<NSString *> *> *' to 'NSArray *')</string>
-// CHECK: </dict>
-// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
-// CHECK: <string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSSet *'</string>
+// CHECK: <string>Type 'NSArray<NSString *> *' is inferred from this context</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Type 'NSArray<NSString *> *' 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 'NSArray<NSString *> *' which is incompatible with static type 'NSSet *'</string>
// CHECK: <key>message</key>
-// CHECK: <string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSSet *'</string>
+// CHECK: <string>Object has a dynamic type 'NSArray<NSString *> *' which is incompatible with static type 'NSSet *'</string>
// CHECK: </dict>
// CHECK: </array>
-// CHECK: <key>description</key><string>Conversion from value of type 'NSArray<NSString *> *' to incompatible type 'NSSet *'</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 'NSArray<NSString *> *' which is incompatible with static type 'NSSet *'</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>
// 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 'NSString *' is inferred from this context</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Type 'NSString *' 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 'NSString *' which is incompatible with static type 'NSNumber *'</string>
+// CHECK: <key>message</key>
+// CHECK: <string>Object has a dynamic type 'NSString *' which is incompatible with static type 'NSNumber *'</string>
+// CHECK: </dict>
+// CHECK: </array>
+// CHECK: <key>description</key><string>Object has a dynamic type 'NSString *' which is incompatible with static type 'NSNumber *'</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>