vsavchenko updated this revision to Diff 260580.
vsavchenko added a comment.

Move getSelfSVal from CallEvent to ProgramState


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D78286/new/

https://reviews.llvm.org/D78286

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
  clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
  clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
  clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
  clang/lib/StaticAnalyzer/Core/CallEvent.cpp
  clang/lib/StaticAnalyzer/Core/DynamicType.cpp
  clang/lib/StaticAnalyzer/Core/ProgramState.cpp
  clang/test/Analysis/cast-value-state-dump.cpp
  clang/test/Analysis/class-object-state-dump.m
  clang/test/Analysis/inlining/InlineObjCClassMethod.m
  clang/test/Analysis/inlining/ObjCDynTypePopagation.m
  clang/test/Analysis/retain-release-inline.m

Index: clang/test/Analysis/retain-release-inline.m
===================================================================
--- clang/test/Analysis/retain-release-inline.m
+++ clang/test/Analysis/retain-release-inline.m
@@ -13,6 +13,7 @@
 // It includes the basic definitions for the test cases below.
 //===----------------------------------------------------------------------===//
 #define NULL 0
+#define nil ((id)0)
 typedef unsigned int __darwin_natural_t;
 typedef unsigned long uintptr_t;
 typedef unsigned int uint32_t;
@@ -21,14 +22,14 @@
 typedef signed long CFIndex;
 typedef CFIndex CFByteOrder;
 typedef struct {
-    CFIndex location;
-    CFIndex length;
+  CFIndex location;
+  CFIndex length;
 } CFRange;
 static __inline__ __attribute__((always_inline)) CFRange CFRangeMake(CFIndex loc, CFIndex len) {
-    CFRange range;
-    range.location = loc;
-    range.length = len;
-    return range;
+  CFRange range;
+  range.location = loc;
+  range.length = len;
+  return range;
 }
 typedef const void * CFTypeRef;
 typedef const struct __CFString * CFStringRef;
@@ -91,6 +92,7 @@
 - (BOOL)isEqual:(id)object;
 - (id)retain;
 - (oneway void)release;
+- (Class)class;
 - (id)autorelease;
 - (id)init;
 @end  @protocol NSCopying  - (id)copyWithZone:(NSZone *)zone;
@@ -100,6 +102,7 @@
 @interface NSObject <NSObject> {}
 + (id)allocWithZone:(NSZone *)zone;
 + (id)alloc;
++ (Class)class;
 - (void)dealloc;
 @end
 @interface NSObject (NSCoderMethods)
@@ -481,3 +484,33 @@
   [self test_inline_tiny_when_reanalyzing];
 }
 @end
+
+// Original problem: rdar://problem/50739539
+@interface MyClassThatLeaksDuringInit : NSObject
+
++ (MyClassThatLeaksDuringInit *)getAnInstance1;
++ (MyClassThatLeaksDuringInit *)getAnInstance2;
+
+@end
+
+@implementation MyClassThatLeaksDuringInit
+
++ (MyClassThatLeaksDuringInit *)getAnInstance1 {
+  return [[[MyClassThatLeaksDuringInit alloc] init] autorelease]; // expected-warning{{leak}}
+}
+
++ (MyClassThatLeaksDuringInit *)getAnInstance2 {
+  return [[[[self class] alloc] init] autorelease]; // expected-warning{{leak}}
+}
+
+- (instancetype)init {
+  if (1) {
+    return nil;
+  }
+
+  if (nil != (self = [super init])) {
+  }
+  return self;
+}
+
+@end
Index: clang/test/Analysis/inlining/ObjCDynTypePopagation.m
===================================================================
--- clang/test/Analysis/inlining/ObjCDynTypePopagation.m
+++ clang/test/Analysis/inlining/ObjCDynTypePopagation.m
@@ -7,68 +7,67 @@
 PublicSubClass2 *getObj();
 
 @implementation PublicParent
-- (int) getZeroOverridden {
-   return 1;
+- (int)getZeroOverridden {
+  return 1;
 }
-- (int) getZero {
-   return 0;
+- (int)getZero {
+  return 0;
 }
 @end
 
 @implementation PublicSubClass2
-- (int) getZeroOverridden {
-   return 0;
+- (int)getZeroOverridden {
+  return 0;
 }
 
 /* Test that we get the right type from call to alloc. */
-+ (void) testAllocSelf {
++ (void)testAllocSelf {
   id a = [self alloc];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-
-+ (void) testAllocClass {
++ (void)testAllocClass {
   id a = [PublicSubClass2 alloc];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-+ (void) testAllocSuperOverriden {
++ (void)testAllocSuperOverriden {
   id a = [super alloc];
   // Evaluates to 1 in the parent.
-  clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}} 
+  clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}}
 }
 
-+ (void) testAllocSuper {
++ (void)testAllocSuper {
   id a = [super alloc];
   clang_analyzer_eval([a getZero] == 0); // expected-warning{{TRUE}}
 }
 
-+ (void) testAllocInit {
++ (void)testAllocInit {
   id a = [[self alloc] init];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-+ (void) testNewSelf {
++ (void)testNewSelf {
   id a = [self new];
   clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
-// Casting to parent should not pessimize the dynamic type. 
-+ (void) testCastToParent {
- id a = [[self alloc] init];
- PublicParent *p = a;  
+// Casting to parent should not pessimize the dynamic type.
++ (void)testCastToParent {
+  id a = [[self alloc] init];
+  PublicParent *p = a;
   clang_analyzer_eval([p getZeroOverridden] == 0); // expected-warning{{TRUE}}
 }
 
 // The type of parameter gets used.
-+ (void)testTypeFromParam:(PublicParent*) p {
++ (void)testTypeFromParam:(PublicParent *)p {
   clang_analyzer_eval([p getZero] == 0); // expected-warning{{TRUE}}
 }
 
 // Test implicit cast.
 // Note, in this case, p could also be a subclass of MyParent.
-+ (void) testCastFromId:(id) a {
-  PublicParent *p = a;  
++ (void)testCastFromId:(id)a {
+  PublicParent *p = a;
   clang_analyzer_eval([p getZero] == 0); // expected-warning{{TRUE}}
 }
 @end
@@ -76,26 +75,28 @@
 // TODO: Would be nice to handle the case of dynamically obtained class info
 // as well. We need a MemRegion for class types for this.
 int testDynamicClass(BOOL coin) {
- Class AllocClass = (coin ? [NSObject class] : [PublicSubClass2 class]);
- id x = [[AllocClass alloc] init];
- if (coin)
-   return [x getZero];
- return 1;
+  Class AllocClass = (coin ? [NSObject class] : [PublicSubClass2 class]);
+  id x = [[AllocClass alloc] init];
+  if (coin)
+    return [x getZero];
+  return 1;
 }
 
 @interface UserClass : NSObject
-- (PublicSubClass2 *) _newPublicSubClass2;
-- (int) getZero;
-- (void) callNew;
+- (PublicSubClass2 *)_newPublicSubClass2;
+- (int)getZero;
+- (void)callNew;
 @end
 
 @implementation UserClass
-- (PublicSubClass2 *) _newPublicSubClass2 {
+- (PublicSubClass2 *)_newPublicSubClass2 {
   return [[PublicSubClass2 alloc] init];
 }
-- (int) getZero { return 5; }
-- (void) callNew {
+- (int)getZero {
+  return 5;
+}
+- (void)callNew {
   PublicSubClass2 *x = [self _newPublicSubClass2];
   clang_analyzer_eval([x getZero] == 0); //expected-warning{{TRUE}}
 }
 @end
\ No newline at end of file
Index: clang/test/Analysis/inlining/InlineObjCClassMethod.m
===================================================================
--- clang/test/Analysis/inlining/InlineObjCClassMethod.m
+++ clang/test/Analysis/inlining/InlineObjCClassMethod.m
@@ -6,18 +6,25 @@
 // Test inlining of ObjC class methods.
 
 typedef signed char BOOL;
+#define YES ((BOOL)1)
+#define NO ((BOOL)0)
 typedef struct objc_class *Class;
 typedef struct objc_object {
-    Class isa;
-} *id;
-@protocol NSObject  - (BOOL)isEqual:(id)object; @end
+  Class isa;
+} * id;
+@protocol NSObject
+- (BOOL)isEqual:(id)object;
+@end
 @interface NSObject <NSObject> {}
-+(id)alloc;
--(id)init;
--(id)autorelease;
--(id)copy;
++ (id)alloc;
++ (Class)class;
++ (Class)superclass;
+- (id)init;
+- (id)autorelease;
+- (id)copy;
 - (Class)class;
--(id)retain;
+- (instancetype)self;
+- (id)retain;
 @end
 
 // Vanila: ObjC class method is called by name.
@@ -133,10 +140,7 @@
 }
 @end
 
-
-// False negative.
 // ObjC class method call through a decl with a known type.
-// We should be able to track the type of currentClass and inline this call.
 // Note, [self class] could be a subclass. Do we still want to inline here?
 @interface MyClassKT : NSObject
 @end
@@ -152,7 +156,7 @@
 - (int)testClassMethodByKnownVarDecl {
   Class currentClass = [self class];
   int y = [currentClass getInt];
-  return 5/y; // Would be great to get a warning here.
+  return 5 / y; // expected-warning{{Division by zero}}
 }
 @end
 
@@ -240,37 +244,124 @@
 +(unsigned)returns30;
 @end
 
-@implementation SelfClassTestParent
--(unsigned)returns10 { return 100; }
-+(unsigned)returns20 { return 100; }
-+(unsigned)returns30 { return 100; }
+@interface SelfClassTest : SelfClassTestParent
+- (unsigned)returns10;
++ (unsigned)returns20;
++ (unsigned)returns30;
 @end
 
-@interface SelfClassTest : SelfClassTestParent
--(unsigned)returns10;
-+(unsigned)returns20;
-+(unsigned)returns30;
+@implementation SelfClassTestParent
+- (unsigned)returns10 {
+  return 100;
+}
++ (unsigned)returns20 {
+  return 100;
+}
++ (unsigned)returns30 {
+  return 100;
+}
+
+- (void)testSelfReassignment {
+  // Check that we didn't hardcode type for self.
+  self = [[[SelfClassTest class] alloc] init];
+  Class actuallyChildClass = [self class];
+  unsigned result = [actuallyChildClass returns30];
+  clang_analyzer_eval(result == 30); // expected-warning{{TRUE}}
+}
 @end
 
 @implementation SelfClassTest
--(unsigned)returns10 { return 10; }
-+(unsigned)returns20 { return 20; }
-+(unsigned)returns30 { return 30; }
-+(void)classMethod {
+- (unsigned)returns10 {
+  return 10;
+}
++ (unsigned)returns20 {
+  return 20;
+}
++ (unsigned)returns30 {
+  return 30;
+}
++ (BOOL)isClass {
+  return YES;
+}
+- (BOOL)isClass {
+  return NO;
+}
++ (SelfClassTest *)create {
+  return [[self alloc] init];
+}
++ (void)classMethod {
   unsigned result1 = [self returns20];
   clang_analyzer_eval(result1 == 20); // expected-warning{{TRUE}}
+
   unsigned result2 = [[self class] returns30];
   clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}}
+
   unsigned result3 = [[super class] returns30];
-  clang_analyzer_eval(result3 == 100); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(result3 == 100); // expected-warning{{TRUE}}
+
+  // Check that class info is propagated with data
+  Class class41 = [self class];
+  Class class42 = class41;
+  unsigned result4 = [class42 returns30];
+  clang_analyzer_eval(result4 == 30); // expected-warning{{TRUE}}
+
+  Class class51 = [super class];
+  Class class52 = class51;
+  unsigned result5 = [class52 returns30];
+  clang_analyzer_eval(result5 == 100); // expected-warning{{TRUE}}
 }
--(void)instanceMethod {
+- (void)instanceMethod {
   unsigned result0 = [self returns10];
   clang_analyzer_eval(result0 == 10); // expected-warning{{TRUE}}
+
   unsigned result2 = [[self class] returns30];
   clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}}
+
   unsigned result3 = [[super class] returns30];
-  clang_analyzer_eval(result3 == 100); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(result3 == 100); // expected-warning{{TRUE}}
+
+  // Check that class info is propagated with data
+  Class class41 = [self class];
+  Class class42 = class41;
+  unsigned result4 = [class42 returns30];
+  clang_analyzer_eval(result4 == 30); // expected-warning{{TRUE}}
+
+  Class class51 = [super class];
+  Class class52 = class51;
+  unsigned result5 = [class52 returns30];
+  clang_analyzer_eval(result5 == 100); // expected-warning{{TRUE}}
+
+  // Check that we inline class methods when class object is a receiver
+  Class class6 = [self class];
+  BOOL calledClassMethod = [class6 isClass];
+  clang_analyzer_eval(calledClassMethod == YES); // expected-warning{{TRUE}}
+
+  // Check that class info is propagated through the 'self' method
+  Class class71 = [self class];
+  Class class72 = [class71 self];
+  unsigned result7 = [class72 returns30];
+  clang_analyzer_eval(result7 == 30); // expected-warning{{TRUE}}
+
+  // Check that 'class' and 'super' info from direct invocation of the
+  // corresponding class methods is propagated with data
+  Class class8 = [SelfClassTest class];
+  unsigned result8 = [class8 returns30];
+  clang_analyzer_eval(result8 == 30); // expected-warning{{TRUE}}
+
+  Class class9 = [SelfClassTest superclass];
+  unsigned result9 = [class9 returns30];
+  clang_analyzer_eval(result9 == 100); // expected-warning{{TRUE}}
+
+  // Check that we get class from a propagated type
+  SelfClassTestParent *selfAsParent10 = [[SelfClassTest alloc] init];
+  Class class10 = [selfAsParent10 class];
+  unsigned result10 = [class10 returns30];
+  clang_analyzer_eval(result10 == 30); // expected-warning{{TRUE}}
+
+  SelfClassTestParent *selfAsParent11 = [[[self class] alloc] init];
+  Class class11 = [selfAsParent11 class];
+  unsigned result11 = [class11 returns30];
+  clang_analyzer_eval(result11 == 30); // expected-warning{{TRUE}}
 }
 @end
 
Index: clang/test/Analysis/class-object-state-dump.m
===================================================================
--- /dev/null
+++ clang/test/Analysis/class-object-state-dump.m
@@ -0,0 +1,38 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN:   -verify %s 2>&1 | FileCheck %s
+
+// expected-no-diagnostics
+
+void clang_analyzer_printState();
+
+@interface NSObject {
+}
++ (id)alloc;
++ (Class)class;
+- (id)init;
+- (Class)class;
+@end
+
+@interface Parent : NSObject
+@end
+@interface Child : Parent
+@end
+
+void foo(id A, id B);
+
+@implementation Child
++ (void)test {
+  id ClassAsID = [self class];
+  id Object = [[ClassAsID alloc] init];
+  Class TheSameClass = [Object class];
+
+  clang_analyzer_printState();
+  // CHECK:      "class_object_types": [
+  // CHECK-NEXT:   { "symbol": "conj_$[[#]]{Class, LC[[#]], S[[#]], #[[#]]}", "dyn_type": "Child", "sub_classable": true },
+  // CHECK-NEXT:   { "symbol": "conj_$[[#]]{Class, LC[[#]], S[[#]], #[[#]]}", "dyn_type": "Child", "sub_classable": true }
+  // CHECK-NEXT: ]
+
+  // Let's make sure that the information is not GC'd away.
+  foo(ClassAsID, TheSameClass);
+}
+@end
Index: clang/test/Analysis/cast-value-state-dump.cpp
===================================================================
--- clang/test/Analysis/cast-value-state-dump.cpp
+++ clang/test/Analysis/cast-value-state-dump.cpp
@@ -37,7 +37,7 @@
   // CHECK:        { "region": "SymRegion{reg_$0<const struct clang::Shape * S>}", "casts": [
   // CHECK-NEXT:     { "from": "const struct clang::Shape *", "to": "const class clang::Circle *", "kind": "success" },
   // CHECK-NEXT:     { "from": "const struct clang::Shape *", "to": "const class clang::Square *", "kind": "fail" }
-  // CHECK-NEXT:   ]}
+  // CHECK-NEXT:   ] }
 
   (void)(1 / !C);
   // expected-note@-1 {{'C' is non-null}}
Index: clang/lib/StaticAnalyzer/Core/ProgramState.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -240,6 +240,13 @@
   return makeWithStore(NewStore);
 }
 
+SVal ProgramState::getSelfSVal(const LocationContext *LCtx) const {
+  const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
+  if (!SelfDecl)
+    return SVal();
+  return getSVal(getRegion(SelfDecl, LCtx));
+}
+
 SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const {
   // We only want to do fetches from regions that we can actually bind
   // values.  For example, SymbolicRegions of type 'id<...>' cannot
Index: clang/lib/StaticAnalyzer/Core/DynamicType.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/DynamicType.cpp
+++ clang/lib/StaticAnalyzer/Core/DynamicType.cpp
@@ -34,6 +34,10 @@
 REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *,
                                CastSet)
 
+// A map from Class object symbols to the most likely pointed-to type.
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicClassObjectMap, clang::ento::SymbolRef,
+                               clang::ento::DynamicTypeInfo)
+
 namespace clang {
 namespace ento {
 
@@ -76,6 +80,12 @@
   return nullptr;
 }
 
+DynamicTypeInfo getClassObjectDynamicTypeInfo(ProgramStateRef State,
+                                              SymbolRef Sym) {
+  const DynamicTypeInfo *DTI = State->get<DynamicClassObjectMap>(Sym);
+  return DTI ? *DTI : DynamicTypeInfo{};
+}
+
 ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
                                    DynamicTypeInfo NewTy) {
   State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy);
@@ -118,111 +128,165 @@
   return State;
 }
 
+ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
+                                              SymbolRef Sym,
+                                              DynamicTypeInfo NewTy) {
+  State = State->set<DynamicClassObjectMap>(Sym, NewTy);
+  return State;
+}
+
+ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
+                                              SymbolRef Sym, QualType NewTy,
+                                              bool CanBeSubClassed) {
+  return setClassObjectDynamicTypeInfo(State, Sym,
+                                       DynamicTypeInfo(NewTy, CanBeSubClassed));
+}
+
+static bool isLive(SymbolReaper &SR, const MemRegion *MR) {
+  return SR.isLiveRegion(MR);
+}
+
+static bool isLive(SymbolReaper &SR, SymbolRef Sym) { return SR.isLive(Sym); }
+
 template <typename MapTy>
-ProgramStateRef removeDead(ProgramStateRef State, const MapTy &Map,
-                           SymbolReaper &SR) {
+static ProgramStateRef removeDeadImpl(ProgramStateRef State, SymbolReaper &SR) {
+  const auto &Map = State->get<MapTy>();
+
   for (const auto &Elem : Map)
-    if (!SR.isLiveRegion(Elem.first))
-      State = State->remove<DynamicCastMap>(Elem.first);
+    if (!isLive(SR, Elem.first))
+      State = State->remove<MapTy>(Elem.first);
 
   return State;
 }
 
 ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) {
-  return removeDead(State, State->get<DynamicTypeMap>(), SR);
+  return removeDeadImpl<DynamicTypeMap>(State, SR);
 }
 
 ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) {
-  return removeDead(State, State->get<DynamicCastMap>(), SR);
+  return removeDeadImpl<DynamicCastMap>(State, SR);
 }
 
-static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
-                                  const char *NL, unsigned int Space,
-                                  bool IsDot) {
-  Indent(Out, Space, IsDot) << "\"dynamic_types\": ";
+ProgramStateRef removeDeadClassObjectTypes(ProgramStateRef State,
+                                           SymbolReaper &SR) {
+  return removeDeadImpl<DynamicClassObjectMap>(State, SR);
+}
 
-  const DynamicTypeMapTy &Map = State->get<DynamicTypeMap>();
-  if (Map.isEmpty()) {
-    Out << "null," << NL;
-    return;
-  }
+//===----------------------------------------------------------------------===//
+//               Implementation of the 'printer-to-JSON' function
+//===----------------------------------------------------------------------===//
 
-  ++Space;
-  Out << '[' << NL;
-  for (DynamicTypeMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
-    const MemRegion *MR = I->first;
-    const DynamicTypeInfo &DTI = I->second;
-    Indent(Out, Space, IsDot)
-        << "{ \"region\": \"" << MR << "\", \"dyn_type\": ";
-    if (!DTI.isValid()) {
-      Out << "null";
-    } else {
-      Out << '\"' << DTI.getType()->getPointeeType().getAsString()
-          << "\", \"sub_classable\": "
-          << (DTI.canBeASubClass() ? "true" : "false");
-    }
-    Out << " }";
-
-    if (std::next(I) != Map.end())
-      Out << ',';
-    Out << NL;
+static raw_ostream &printJson(const MemRegion *Region, raw_ostream &Out,
+                              const char *NL, unsigned int Space, bool IsDot) {
+  return Out << "\"region\": \"" << Region << "\"";
+}
+
+static raw_ostream &printJson(const SymExpr *Symbol, raw_ostream &Out,
+                              const char *NL, unsigned int Space, bool IsDot) {
+  return Out << "\"symbol\": \"" << Symbol << "\"";
+}
+
+static raw_ostream &printJson(const DynamicTypeInfo &DTI, raw_ostream &Out,
+                              const char *NL, unsigned int Space, bool IsDot) {
+  Out << "\"dyn_type\": ";
+  if (!DTI.isValid()) {
+    Out << "null";
+  } else {
+    QualType ToPrint = DTI.getType();
+    if (ToPrint->isAnyPointerType())
+      ToPrint = ToPrint->getPointeeType();
+
+    Out << '\"' << ToPrint.getAsString() << "\", \"sub_classable\": "
+        << (DTI.canBeASubClass() ? "true" : "false");
   }
+  return Out;
+}
 
-  --Space;
-  Indent(Out, Space, IsDot) << "]," << NL;
+static raw_ostream &printJson(const DynamicCastInfo &DCI, raw_ostream &Out,
+                              const char *NL, unsigned int Space, bool IsDot) {
+  return Out << "\"from\": \"" << DCI.from().getAsString() << "\", \"to\": \""
+             << DCI.to().getAsString() << "\", \"kind\": \""
+             << (DCI.succeeds() ? "success" : "fail") << "\"";
 }
 
-static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
-                                  const char *NL, unsigned int Space,
-                                  bool IsDot) {
-  Indent(Out, Space, IsDot) << "\"dynamic_casts\": ";
+template <class T, class U>
+static raw_ostream &printJson(const std::pair<T, U> &Pair, raw_ostream &Out,
+                              const char *NL, unsigned int Space, bool IsDot) {
+  printJson(Pair.first, Out, NL, Space, IsDot) << ", ";
+  return printJson(Pair.second, Out, NL, Space, IsDot);
+}
 
-  const DynamicCastMapTy &Map = State->get<DynamicCastMap>();
-  if (Map.isEmpty()) {
-    Out << "null," << NL;
-    return;
+template <class ContainerTy>
+static raw_ostream &printJsonContainer(const ContainerTy &Container,
+                                       raw_ostream &Out, const char *NL,
+                                       unsigned int Space, bool IsDot) {
+  if (Container.isEmpty()) {
+    return Out << "null";
   }
 
   ++Space;
   Out << '[' << NL;
-  for (DynamicCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
-    const MemRegion *MR = I->first;
-    const CastSet &Set = I->second;
-
-    Indent(Out, Space, IsDot) << "{ \"region\": \"" << MR << "\", \"casts\": ";
-    if (Set.isEmpty()) {
-      Out << "null ";
-    } else {
-      ++Space;
-      Out << '[' << NL;
-      for (CastSet::iterator SI = Set.begin(); SI != Set.end(); ++SI) {
-        Indent(Out, Space, IsDot)
-            << "{ \"from\": \"" << SI->from().getAsString() << "\", \"to\": \""
-            << SI->to().getAsString() << "\", \"kind\": \""
-            << (SI->succeeds() ? "success" : "fail") << "\" }";
-
-        if (std::next(SI) != Set.end())
-          Out << ',';
-        Out << NL;
-      }
-      --Space;
-      Indent(Out, Space, IsDot) << ']';
-    }
-    Out << '}';
-
-    if (std::next(I) != Map.end())
+  for (auto I = Container.begin(); I != Container.end(); ++I) {
+    const auto &Element = *I;
+
+    Indent(Out, Space, IsDot) << "{ ";
+    printJson(Element, Out, NL, Space, IsDot) << " }";
+
+    if (std::next(I) != Container.end())
       Out << ',';
     Out << NL;
   }
 
   --Space;
-  Indent(Out, Space, IsDot) << "]," << NL;
+  return Indent(Out, Space, IsDot) << "]";
+}
+
+static raw_ostream &printJson(const CastSet &Set, raw_ostream &Out,
+                              const char *NL, unsigned int Space, bool IsDot) {
+  Out << "\"casts\": ";
+  return printJsonContainer(Set, Out, NL, Space, IsDot);
+}
+
+template <class MapTy>
+static void printJsonImpl(raw_ostream &Out, ProgramStateRef State,
+                          const char *Name, const char *NL, unsigned int Space,
+                          bool IsDot, bool PrintEvenIfEmpty = true) {
+  const auto &Map = State->get<MapTy>();
+  if (Map.isEmpty() && !PrintEvenIfEmpty)
+    return;
+
+  Indent(Out, Space, IsDot) << "\"" << Name << "\": ";
+  printJsonContainer(Map, Out, NL, Space, IsDot) << "," << NL;
+}
+
+static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
+                                  const char *NL, unsigned int Space,
+                                  bool IsDot) {
+  printJsonImpl<DynamicTypeMap>(Out, State, "dynamic_types", NL, Space, IsDot);
+}
+
+static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
+                                  const char *NL, unsigned int Space,
+                                  bool IsDot) {
+  printJsonImpl<DynamicCastMap>(Out, State, "dynamic_casts", NL, Space, IsDot);
+}
+
+static void printClassObjectDynamicTypesJson(raw_ostream &Out,
+                                             ProgramStateRef State,
+                                             const char *NL, unsigned int Space,
+                                             bool IsDot) {
+  // Let's print Class object type information only if we have something
+  // meaningful to print.
+  printJsonImpl<DynamicClassObjectMap>(Out, State, "class_object_types", NL,
+                                       Space, IsDot,
+                                       /*PrintEvenIfEmpty=*/false);
 }
 
 void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
                               const char *NL, unsigned int Space, bool IsDot) {
   printDynamicTypesJson(Out, State, NL, Space, IsDot);
   printDynamicCastsJson(Out, State, NL, Space, IsDot);
+  printClassObjectDynamicTypesJson(Out, State, NL, Space, IsDot);
 }
 
 } // namespace ento
Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -972,14 +972,6 @@
   Values.push_back(getReceiverSVal());
 }
 
-SVal ObjCMethodCall::getSelfSVal() const {
-  const LocationContext *LCtx = getLocationContext();
-  const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
-  if (!SelfDecl)
-    return SVal();
-  return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
-}
-
 SVal ObjCMethodCall::getReceiverSVal() const {
   // FIXME: Is this the best way to handle class receivers?
   if (!isInstanceMessage())
@@ -991,7 +983,7 @@
   // An instance message with no expression means we are sending to super.
   // In this case the object reference is the same as 'self'.
   assert(getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance);
-  SVal SelfVal = getSelfSVal();
+  SVal SelfVal = getState()->getSelfSVal(getLocationContext());
   assert(SelfVal.isValid() && "Calling super but not in ObjC method");
   return SelfVal;
 }
@@ -1005,8 +997,9 @@
     return false;
 
   SVal RecVal = getSVal(getOriginExpr()->getInstanceReceiver());
+  SVal SelfVal = getState()->getSelfSVal(getLocationContext());
 
-  return (RecVal == getSelfSVal());
+  return (RecVal == SelfVal);
 }
 
 SourceRange ObjCMethodCall::getSourceRange() const {
@@ -1173,23 +1166,75 @@
   return MD;
 }
 
-static bool isCallToSelfClass(const ObjCMessageExpr *ME) {
-  const Expr* InstRec = ME->getInstanceReceiver();
-  if (!InstRec)
-    return false;
-  const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts());
+struct PrivateMethodKey {
+  const ObjCInterfaceDecl *Interface;
+  Selector LookupSelector;
+  bool IsClassMethod;
+};
 
-  // Check that receiver is called 'self'.
-  if (!InstRecIg || !InstRecIg->getFoundDecl() ||
-      !InstRecIg->getFoundDecl()->getName().equals("self"))
-    return false;
+template <> struct llvm::DenseMapInfo<PrivateMethodKey> {
+  using InterfaceInfo = DenseMapInfo<const ObjCInterfaceDecl *>;
+  using SelectorInfo = DenseMapInfo<Selector>;
 
-  // Check that the method name is 'class'.
-  if (ME->getSelector().getNumArgs() != 0 ||
-      !ME->getSelector().getNameForSlot(0).equals("class"))
-    return false;
+  static inline PrivateMethodKey getEmptyKey() {
+    return {InterfaceInfo::getEmptyKey(), SelectorInfo::getEmptyKey(), false};
+  }
 
-  return true;
+  static inline PrivateMethodKey getTombstoneKey() {
+    return {InterfaceInfo::getTombstoneKey(), SelectorInfo::getTombstoneKey(),
+            true};
+  }
+
+  static unsigned getHashValue(const PrivateMethodKey &Key) {
+    return llvm::hash_combine(
+        llvm::hash_code(InterfaceInfo::getHashValue(Key.Interface)),
+        llvm::hash_code(SelectorInfo::getHashValue(Key.LookupSelector)),
+        Key.IsClassMethod);
+  }
+
+  static bool isEqual(const PrivateMethodKey &LHS,
+                      const PrivateMethodKey &RHS) {
+    return InterfaceInfo::isEqual(LHS.Interface, RHS.Interface) &&
+           SelectorInfo::isEqual(LHS.LookupSelector, RHS.LookupSelector) &&
+           LHS.IsClassMethod == RHS.IsClassMethod;
+  }
+};
+
+const ObjCMethodDecl *
+lookupRuntimeDefinition(const ObjCInterfaceDecl *Interface,
+                        Selector LookupSelector, bool InstanceMethod) {
+  // Repeatedly calling lookupPrivateMethod() is expensive, especially
+  // when in many cases it returns null.  We cache the results so
+  // that repeated queries on the same ObjCIntefaceDecl and Selector
+  // don't incur the same cost.  On some test cases, we can see the
+  // same query being issued thousands of times.
+  //
+  // NOTE: This cache is essentially a "global" variable, but it
+  // only gets lazily created when we get here.  The value of the
+  // cache probably comes from it being global across ExprEngines,
+  // where the same queries may get issued.  If we are worried about
+  // concurrency, or possibly loading/unloading ASTs, etc., we may
+  // need to revisit this someday.  In terms of memory, this table
+  // stays around until clang quits, which also may be bad if we
+  // need to release memory.
+  using PrivateMethodCache =
+      llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
+
+  static PrivateMethodCache PMC;
+  Optional<const ObjCMethodDecl *> &Val =
+      PMC[{Interface, LookupSelector, InstanceMethod}];
+
+  // Query lookupPrivateMethod() if the cache does not hit.
+  if (!Val.hasValue()) {
+    Val = Interface->lookupPrivateMethod(LookupSelector, InstanceMethod);
+
+    if (!*Val) {
+      // Query 'lookupMethod' as a backup.
+      Val = Interface->lookupMethod(LookupSelector, InstanceMethod);
+    }
+  }
+
+  return Val.getValue();
 }
 
 RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
@@ -1199,8 +1244,9 @@
 
   if (E->isInstanceMessage()) {
     // Find the receiver type.
-    const ObjCObjectPointerType *ReceiverT = nullptr;
+    const ObjCObjectType *ReceiverT = nullptr;
     bool CanBeSubClassed = false;
+    bool LookingForInstanceMethod = true;
     QualType SupersType = E->getSuperType();
     const MemRegion *Receiver = nullptr;
 
@@ -1208,7 +1254,7 @@
       // The receiver is guaranteed to be 'super' in this case.
       // Super always means the type of immediate predecessor to the method
       // where the call occurs.
-      ReceiverT = cast<ObjCObjectPointerType>(SupersType);
+      ReceiverT = cast<ObjCObjectPointerType>(SupersType)->getObjectType();
     } else {
       Receiver = getReceiverSVal().getAsRegion();
       if (!Receiver)
@@ -1223,100 +1269,59 @@
 
       QualType DynType = DTI.getType();
       CanBeSubClassed = DTI.canBeASubClass();
-      ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
 
-      if (ReceiverT && CanBeSubClassed)
-        if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
-          if (!canBeOverridenInSubclass(IDecl, Sel))
-            CanBeSubClassed = false;
-    }
+      const auto *ReceiverDynT =
+          dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
+
+      if (ReceiverDynT) {
+        ReceiverT = ReceiverDynT->getObjectType();
+
+        // It can be actually class methods called with Class object as a
+        // receiver. This type of messages is treated by the compiler as
+        // instance (not class).
+        if (ReceiverT->isObjCClass()) {
 
-    // Handle special cases of '[self classMethod]' and
-    // '[[self class] classMethod]', which are treated by the compiler as
-    // instance (not class) messages. We will statically dispatch to those.
-    if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) {
-      // For [self classMethod], return the compiler visible declaration.
-      if (PT->getObjectType()->isObjCClass() &&
-          Receiver == getSelfSVal().getAsRegion())
-        return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
-
-      // Similarly, handle [[self class] classMethod].
-      // TODO: We are currently doing a syntactic match for this pattern with is
-      // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m
-      // shows. A better way would be to associate the meta type with the symbol
-      // using the dynamic type info tracking and use it here. We can add a new
-      // SVal for ObjC 'Class' values that know what interface declaration they
-      // come from. Then 'self' in a class method would be filled in with
-      // something meaningful in ObjCMethodCall::getReceiverSVal() and we could
-      // do proper dynamic dispatch for class methods just like we do for
-      // instance methods now.
-      if (E->getInstanceReceiver())
-        if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver()))
-          if (isCallToSelfClass(M))
+          SVal SelfVal = getState()->getSelfSVal(getLocationContext());
+          // For [self classMethod], return compiler visible declaration.
+          if (Receiver == SelfVal.getAsRegion()) {
             return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+          }
+
+          // Otherwise, let's check if we know something about the type
+          // inside of this class object.
+          if (SymbolRef ReceiverSym = getReceiverSVal().getAsSymbol()) {
+            DynamicTypeInfo DTI =
+                getClassObjectDynamicTypeInfo(getState(), ReceiverSym);
+            if (DTI.isValid()) {
+              // Let's use this type for lookup.
+              ReceiverT =
+                  cast<ObjCObjectType>(DTI.getType().getCanonicalType());
+
+              CanBeSubClassed = DTI.canBeASubClass();
+              // And it should be a class method instead.
+              LookingForInstanceMethod = false;
+            }
+          }
+        }
+
+        if (CanBeSubClassed)
+          if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterface())
+            // Even if `DynamicTypeInfo` told us that it can be
+            // not necessarily this type, but its descendants, we still want
+            // to check again if this selector can be actually overridden.
+            CanBeSubClassed = canBeOverridenInSubclass(IDecl, Sel);
+      }
     }
 
     // Lookup the instance method implementation.
     if (ReceiverT)
-      if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
-        // Repeatedly calling lookupPrivateMethod() is expensive, especially
-        // when in many cases it returns null.  We cache the results so
-        // that repeated queries on the same ObjCIntefaceDecl and Selector
-        // don't incur the same cost.  On some test cases, we can see the
-        // same query being issued thousands of times.
-        //
-        // NOTE: This cache is essentially a "global" variable, but it
-        // only gets lazily created when we get here.  The value of the
-        // cache probably comes from it being global across ExprEngines,
-        // where the same queries may get issued.  If we are worried about
-        // concurrency, or possibly loading/unloading ASTs, etc., we may
-        // need to revisit this someday.  In terms of memory, this table
-        // stays around until clang quits, which also may be bad if we
-        // need to release memory.
-        using PrivateMethodKey = std::pair<const ObjCInterfaceDecl *, Selector>;
-        using PrivateMethodCache =
-            llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
-
-        static PrivateMethodCache PMC;
-        Optional<const ObjCMethodDecl *> &Val = PMC[std::make_pair(IDecl, Sel)];
-
-        // Query lookupPrivateMethod() if the cache does not hit.
-        if (!Val.hasValue()) {
-          Val = IDecl->lookupPrivateMethod(Sel);
-
-          // If the method is a property accessor, we should try to "inline" it
-          // even if we don't actually have an implementation.
-          if (!*Val)
-            if (const ObjCMethodDecl *CompileTimeMD = E->getMethodDecl())
-              if (CompileTimeMD->isPropertyAccessor()) {
-                if (!CompileTimeMD->getSelfDecl() &&
-                    isa<ObjCCategoryDecl>(CompileTimeMD->getDeclContext())) {
-                  // If the method is an accessor in a category, and it doesn't
-                  // have a self declaration, first
-                  // try to find the method in a class extension. This
-                  // works around a bug in Sema where multiple accessors
-                  // are synthesized for properties in class
-                  // extensions that are redeclared in a category and the
-                  // the implicit parameters are not filled in for
-                  // the method on the category.
-                  // This ensures we find the accessor in the extension, which
-                  // has the implicit parameters filled in.
-                  auto *ID = CompileTimeMD->getClassInterface();
-                  for (auto *CatDecl : ID->visible_extensions()) {
-                    Val = CatDecl->getMethod(Sel,
-                                             CompileTimeMD->isInstanceMethod());
-                    if (*Val)
-                      break;
-                  }
-                }
-                if (!*Val)
-                  Val = IDecl->lookupInstanceMethod(Sel);
-              }
-        }
+      if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterface()) {
+        const ObjCMethodDecl *MD =
+            lookupRuntimeDefinition(IDecl, Sel, LookingForInstanceMethod);
 
-        const ObjCMethodDecl *MD = Val.getValue();
         if (MD && !MD->hasBody())
           MD = MD->getCanonicalDecl();
+
         if (CanBeSubClassed)
           return RuntimeDefinition(MD, Receiver);
         else
Index: clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
@@ -116,13 +116,14 @@
     return;
 
   ProgramStateRef State = C.getState();
-  SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol();
-  assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?");
+  const LocationContext *LC = C.getLocationContext();
+  SymbolRef SelfSymbol = State->getSelfSVal(LC).getAsSymbol();
+  assert(SelfSymbol && "No receiver symbol at call to [super dealloc]?");
 
   // We add this transition in checkPostObjCMessage to avoid warning when
   // we inline a call to [super dealloc] where the inlined call itself
   // calls [super dealloc].
-  State = State->add<CalledSuperDealloc>(ReceiverSymbol);
+  State = State->add<CalledSuperDealloc>(SelfSymbol);
   C.addTransition(State);
 }
 
Index: clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -109,11 +109,127 @@
   /// This value is set to true, when the Generics checker is turned on.
   DefaultBool CheckGenerics;
 };
+
+bool isObjCClassType(QualType Type) {
+  if (const auto *PointerType = dyn_cast<ObjCObjectPointerType>(Type)) {
+    return PointerType->getObjectType()->isObjCClass();
+  }
+  return false;
+}
+
+struct RuntimeType {
+  const ObjCObjectType *Type = nullptr;
+  bool Precise = false;
+
+  operator bool() const { return Type != nullptr; }
+};
+
+RuntimeType inferReceiverType(const ObjCMethodCall &Message,
+                              CheckerContext &C) {
+  const ObjCMessageExpr *MessageExpr = Message.getOriginExpr();
+
+  // Check if we can statically infer the actual type precisely.
+  //
+  // 1. Class is written directly in the message:
+  // \code
+  //   [ActualClass classMethod];
+  // \endcode
+  if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class) {
+    return {MessageExpr->getClassReceiver()->getAs<ObjCObjectType>(),
+            /*Precise=*/true};
+  }
+
+  // 2. Receiver is 'super' from a class method (a.k.a 'super' is a
+  //    class object).
+  // \code
+  //   [super classMethod];
+  // \endcode
+  if (MessageExpr->getReceiverKind() == ObjCMessageExpr::SuperClass) {
+    return {MessageExpr->getSuperType()->getAs<ObjCObjectType>(),
+            /*Precise=*/true};
+  }
+
+  // 3. Receiver is 'super' from an instance method (a.k.a 'super' is an
+  //    instance of a super class).
+  // \code
+  //   [super instanceMethod];
+  // \encode
+  if (MessageExpr->getReceiverKind() == ObjCMessageExpr::SuperInstance) {
+    if (const auto *ObjTy =
+            MessageExpr->getSuperType()->getAs<ObjCObjectPointerType>())
+      return {ObjTy->getObjectType(), /*Precise=*/true};
+  }
+
+  const Expr *RecE = MessageExpr->getInstanceReceiver();
+
+  if (!RecE)
+    return {};
+
+  // Otherwise, let's try to get type information from our estimations of
+  // runtime types.
+  QualType InferredType;
+  SVal ReceiverSVal = C.getSVal(RecE);
+  ProgramStateRef State = C.getState();
+
+  if (const MemRegion *ReceiverRegion = ReceiverSVal.getAsRegion()) {
+    if (DynamicTypeInfo DTI = getDynamicTypeInfo(State, ReceiverRegion)) {
+      InferredType = DTI.getType().getCanonicalType();
+    }
+  }
+
+  if (SymbolRef ReceiverSymbol = ReceiverSVal.getAsSymbol()) {
+    if (InferredType.isNull()) {
+      InferredType = ReceiverSymbol->getType();
+    }
+
+    // If receiver is a Class object, we want to figure out the type it
+    // represents.
+    if (isObjCClassType(InferredType)) {
+      // We actually might have some info on what type is contained in there.
+      if (DynamicTypeInfo DTI =
+              getClassObjectDynamicTypeInfo(State, ReceiverSymbol)) {
+
+        // Types in Class objects can be ONLY Objective-C types
+        return {cast<ObjCObjectType>(DTI.getType()), !DTI.canBeASubClass()};
+      }
+
+      SVal SelfSVal = State->getSelfSVal(C.getLocationContext());
+
+      // Another way we can guess what is in Class object, is when it is a
+      // 'self' variable of the current class method.
+      if (ReceiverSVal == SelfSVal) {
+        // In this case, we should return the type of the enclosing class
+        // declaration.
+        if (const ObjCMethodDecl *MD =
+                dyn_cast<ObjCMethodDecl>(C.getStackFrame()->getDecl()))
+          if (const ObjCObjectType *ObjTy = dyn_cast<ObjCObjectType>(
+                  MD->getClassInterface()->getTypeForDecl()))
+            return {ObjTy};
+      }
+    }
+  }
+
+  // Unfortunately, it seems like we have no idea what that type is.
+  if (InferredType.isNull()) {
+    return {};
+  }
+
+  // We can end up here if we got some dynamic type info and the
+  // receiver is not one of the known Class objects.
+  if (const auto *ReceiverInferredType =
+          dyn_cast<ObjCObjectPointerType>(InferredType)) {
+    return {ReceiverInferredType->getObjectType()};
+  }
+
+  // Any other type (like 'Class') is not really useful at this point.
+  return {};
+}
 } // end anonymous namespace
 
 void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR,
                                               CheckerContext &C) const {
   ProgramStateRef State = removeDeadTypes(C.getState(), SR);
+  State = removeDeadClassObjectTypes(State, SR);
 
   MostSpecializedTypeArgsMapTy TyArgMap =
       State->get<MostSpecializedTypeArgsMap>();
@@ -209,12 +325,21 @@
       case OMF_alloc:
       case OMF_new: {
         // Get the type of object that will get created.
-        const ObjCMessageExpr *MsgE = Msg->getOriginExpr();
-        const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C);
+        RuntimeType ObjTy = inferReceiverType(*Msg, C);
+
         if (!ObjTy)
           return;
+
         QualType DynResTy =
-                 C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0));
+            C.getASTContext().getObjCObjectPointerType(QualType(ObjTy.Type, 0));
+        // We used to assume that whatever type we got from inferring the
+        // type is actually precise (and it is not exactly correct).
+        // A big portion of the existing behavior depends on that assumption
+        // (e.g. certain inlining won't take place). For this reason, we don't
+        // use ObjTy.Precise flag here.
+        //
+        // TODO: We should mitigate this problem some time in the future
+        // and replace hardcoded 'false' with '!ObjTy.Precise'.
         C.addTransition(setDynamicTypeInfo(State, RetReg, DynResTy, false));
         break;
       }
@@ -303,40 +428,6 @@
                                      /*CanBeSubClassed=*/false));
 }
 
-const ObjCObjectType *
-DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
-                                                    CheckerContext &C) const {
-  if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) {
-    if (const ObjCObjectType *ObjTy
-          = MsgE->getClassReceiver()->getAs<ObjCObjectType>())
-    return ObjTy;
-  }
-
-  if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) {
-    if (const ObjCObjectType *ObjTy
-          = MsgE->getSuperType()->getAs<ObjCObjectType>())
-      return ObjTy;
-  }
-
-  const Expr *RecE = MsgE->getInstanceReceiver();
-  if (!RecE)
-    return nullptr;
-
-  RecE= RecE->IgnoreParenImpCasts();
-  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) {
-    const StackFrameContext *SFCtx = C.getStackFrame();
-    // Are we calling [self alloc]? If this is self, get the type of the
-    // enclosing ObjC class.
-    if (DRE->getDecl() == SFCtx->getSelfDecl()) {
-      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl()))
-        if (const ObjCObjectType *ObjTy =
-            dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl()))
-          return ObjTy;
-    }
-  }
-  return nullptr;
-}
-
 // Return a better dynamic type if one can be derived from the cast.
 // Compare the current dynamic type of the region and the new type to which we
 // are casting. If the new type is lower in the inheritance hierarchy, pick it.
@@ -821,25 +912,56 @@
 
   Selector Sel = MessageExpr->getSelector();
   ProgramStateRef State = C.getState();
-  // Inference for class variables.
-  // We are only interested in cases where the class method is invoked on a
-  // class. This method is provided by the runtime and available on all classes.
-  if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class &&
-      Sel.getAsString() == "class") {
-    QualType ReceiverType = MessageExpr->getClassReceiver();
-    const auto *ReceiverClassType = ReceiverType->castAs<ObjCObjectType>();
-    if (!ReceiverClassType->isSpecialized())
-      return;
 
-    QualType ReceiverClassPointerType =
-        C.getASTContext().getObjCObjectPointerType(
-            QualType(ReceiverClassType, 0));
-    const auto *InferredType =
-        ReceiverClassPointerType->castAs<ObjCObjectPointerType>();
+  // Here we try to propagate information on Class objects.
+  if (Sel.getAsString() == "class") {
+    // We try to figure out the type from the receiver of the 'class' message.
+    if (RuntimeType ReceiverRuntimeType = inferReceiverType(M, C)) {
+
+      ReceiverRuntimeType.Type->getSuperClassType();
+      QualType ReceiverClassType(ReceiverRuntimeType.Type, 0);
+
+      // We want to consider only precise information on generics.
+      if (ReceiverRuntimeType.Type->isSpecialized() &&
+          ReceiverRuntimeType.Precise) {
+        QualType ReceiverClassPointerType =
+            C.getASTContext().getObjCObjectPointerType(ReceiverClassType);
+        const auto *InferredType =
+            ReceiverClassPointerType->castAs<ObjCObjectPointerType>();
+        State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType);
+      }
+
+      // Constrain the resulting class object to the inferred type.
+      State = setClassObjectDynamicTypeInfo(State, RetSym, ReceiverClassType,
+                                            !ReceiverRuntimeType.Precise);
 
-    State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType);
-    C.addTransition(State);
-    return;
+      C.addTransition(State);
+      return;
+    }
+  }
+
+  if (Sel.getAsString() == "superclass") {
+    // We try to figure out the type from the receiver of the 'superclass'
+    // message.
+    if (RuntimeType ReceiverRuntimeType = inferReceiverType(M, C)) {
+
+      // Result type would be a super class of the receiver's type.
+      QualType ReceiversSuperClass =
+          ReceiverRuntimeType.Type->getSuperClassType();
+
+      // Check if it really had super class.
+      //
+      // TODO: we can probably pay closer attention to cases when the class
+      // object can be 'nil' as the result of such message.
+      if (!ReceiversSuperClass.isNull()) {
+        // Constrain the resulting class object to the inferred type.
+        State = setClassObjectDynamicTypeInfo(
+            State, RetSym, ReceiversSuperClass, !ReceiverRuntimeType.Precise);
+
+        C.addTransition(State);
+      }
+      return;
+    }
   }
 
   // Tracking for return types.
Index: clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -30,7 +30,7 @@
 using namespace ento;
 
 namespace {
-class CastValueChecker : public Checker<eval::Call> {
+class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> {
   enum class CallKind { Function, Method, InstanceOf };
 
   using CastCheck =
@@ -51,6 +51,7 @@
   // 1) isa:             The parameter is non-null, returns boolean.
   // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.
   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
 
 private:
   // These are known in the LLVM project. The pairs are in the following form:
@@ -432,6 +433,11 @@
   return true;
 }
 
+void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,
+                                        CheckerContext &C) const {
+  C.addTransition(removeDeadCasts(C.getState(), SR));
+}
+
 void ento::registerCastValueChecker(CheckerManager &Mgr) {
   Mgr.registerChecker<CastValueChecker>();
 }
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -298,6 +298,9 @@
   LLVM_NODISCARD ProgramStateRef enterStackFrame(
       const CallEvent &Call, const StackFrameContext *CalleeCtx) const;
 
+  /// Return the value of 'self' if available in the given context.
+  SVal getSelfSVal(const LocationContext *LC) const;
+
   /// Get the lvalue for a base class object reference.
   Loc getLValue(const CXXBaseSpecifier &BaseSpec, const SubRegion *Super) const;
 
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
@@ -33,6 +33,8 @@
   /// Returns the currently inferred upper bound on the runtime type.
   QualType getType() const { return DynTy; }
 
+  operator bool() const { return isValid(); }
+
   bool operator==(const DynamicTypeInfo &RHS) const {
     return DynTy == RHS.DynTy && CanBeASubClass == RHS.CanBeASubClass;
   }
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
@@ -36,6 +36,10 @@
 const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State,
                                              const MemRegion *MR);
 
+/// Get dynamic type information stored in a class object represented by \p Sym.
+DynamicTypeInfo getClassObjectDynamicTypeInfo(ProgramStateRef State,
+                                              SymbolRef Sym);
+
 /// Get dynamic cast information from \p CastFromTy to \p CastToTy of \p MR.
 const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
                                           const MemRegion *MR,
@@ -50,6 +54,16 @@
 ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
                                    QualType NewTy, bool CanBeSubClassed = true);
 
+/// Set constraint on a type contained in a class object; return the new state.
+ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
+                                              SymbolRef Sym,
+                                              DynamicTypeInfo NewTy);
+
+/// Set constraint on a type contained in a class object; return the new state.
+ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
+                                              SymbolRef Sym, QualType NewTy,
+                                              bool CanBeSubClassed = true);
+
 /// Set dynamic type and cast information of the region; return the new state.
 ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
                                           const MemRegion *MR,
@@ -63,6 +77,10 @@
 /// Removes the dead cast informations from \p State.
 ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR);
 
+/// Removes the dead Class object type informations from \p State.
+ProgramStateRef removeDeadClassObjectTypes(ProgramStateRef State,
+                                           SymbolReaper &SR);
+
 void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
                               const char *NL = "\n", unsigned int Space = 0,
                               bool IsDot = false);
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -1115,9 +1115,6 @@
   /// Returns the value of the receiver at the time of this call.
   SVal getReceiverSVal() const;
 
-  /// Return the value of 'self' if available.
-  SVal getSelfSVal() const;
-
   /// Get the interface for the receiver.
   ///
   /// This works whether this is an instance message or a class message.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D78286: [analyze... Valeriy Savchenko via Phabricator via cfe-commits

Reply via email to