vsavchenko updated this revision to Diff 259314.
vsavchenko marked 9 inline comments as done.
vsavchenko added a comment.
Fix review remarks
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/DynamicType.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
clang/lib/StaticAnalyzer/Core/DynamicType.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/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
@@ -1173,23 +1173,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;
+};
+
+template <> struct llvm::DenseMapInfo<PrivateMethodKey> {
+ using InterfaceInfo = DenseMapInfo<const ObjCInterfaceDecl *>;
+ using SelectorInfo = DenseMapInfo<Selector>;
+
+ static inline PrivateMethodKey getEmptyKey() {
+ return {InterfaceInfo::getEmptyKey(), SelectorInfo::getEmptyKey(), false};
+ }
- // Check that receiver is called 'self'.
- if (!InstRecIg || !InstRecIg->getFoundDecl() ||
- !InstRecIg->getFoundDecl()->getName().equals("self"))
- return false;
+ static inline PrivateMethodKey getTombstoneKey() {
+ return {InterfaceInfo::getTombstoneKey(), SelectorInfo::getTombstoneKey(),
+ true};
+ }
- // Check that the method name is 'class'.
- if (ME->getSelector().getNumArgs() != 0 ||
- !ME->getSelector().getNameForSlot(0).equals("class"))
- return false;
+ 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);
+ }
- return true;
+ 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 +1251,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 +1261,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 +1276,58 @@
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();
- // 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))
+ // 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()) {
+
+ // For [self classMethod], return compiler visible declaration.
+ if (Receiver == getSelfSVal().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/DynamicTypePropagation.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -109,11 +109,125 @@
/// 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()};
+ }
+
+ // Another way we can guess what is in Class object, is when it is a
+ // 'self' variable of the current class method.
+ if (ReceiverSVal == Message.getSelfSVal()) {
+ // 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 +323,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 +426,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 +910,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/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);
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits