[PATCH] D63117: [analyzer] RetainCount: Add support for OSRequiredCast().

2019-06-19 Thread Phabricator via Phabricator via cfe-commits
This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was automatically updated to reflect the committed changes.
Closed by commit rL363891: [analyzer] RetainCount: Add support for 
OSRequiredCast(). (authored by dergachev, committed by ).
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

Changed prior to commit:
  https://reviews.llvm.org/D63117?vs=203959=205698#toc

Repository:
  rL LLVM

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

https://reviews.llvm.org/D63117

Files:
  cfe/trunk/lib/Analysis/RetainSummaryManager.cpp
  cfe/trunk/test/Analysis/os_object_base.h
  cfe/trunk/test/Analysis/osobject-retain-release.cpp


Index: cfe/trunk/lib/Analysis/RetainSummaryManager.cpp
===
--- cfe/trunk/lib/Analysis/RetainSummaryManager.cpp
+++ cfe/trunk/lib/Analysis/RetainSummaryManager.cpp
@@ -152,6 +152,10 @@
   return S == "safeMetaCast";
 }
 
+static bool isOSObjectRequiredCast(StringRef S) {
+  return S == "requiredMetaCast";
+}
+
 static bool isOSObjectThisCast(StringRef S) {
   return S == "metaCast";
 }
@@ -234,7 +238,8 @@
   if (RetTy->isPointerType()) {
 const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
 if (PD && isOSObjectSubclass(PD)) {
-  if (isOSObjectDynamicCast(FName) || isOSObjectThisCast(FName))
+  if (isOSObjectDynamicCast(FName) || isOSObjectRequiredCast(FName) ||
+  isOSObjectThisCast(FName))
 return getDefaultSummary();
 
   // TODO: Add support for the slightly common *Matching(table) idiom.
@@ -745,6 +750,8 @@
 if (TrackOSObjects) {
   if (isOSObjectDynamicCast(FName) && FD->param_size() >= 1) {
 return BehaviorSummary::IdentityOrZero;
+  } else if (isOSObjectRequiredCast(FName) && FD->param_size() >= 1) {
+return BehaviorSummary::Identity;
   } else if (isOSObjectThisCast(FName) && isa(FD) &&
  !cast(FD)->isStatic()) {
 return BehaviorSummary::IdentityThis;
Index: cfe/trunk/test/Analysis/os_object_base.h
===
--- cfe/trunk/test/Analysis/os_object_base.h
+++ cfe/trunk/test/Analysis/os_object_base.h
@@ -12,6 +12,8 @@
 
 #define OSDynamicCast(type, inst)   \
 ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type)))
+#define OSRequiredCast(type, inst)   \
+((type *) OSMetaClassBase::requiredMetaCast((inst), OSTypeID(type)))
 
 #define OSTypeAlloc(type)   ((type *) ((type::metaClass)->alloc()))
 
@@ -22,6 +24,8 @@
 struct OSMetaClassBase {
   static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst,
const OSMetaClass *meta);
+  static OSMetaClassBase *requiredMetaCast(const OSMetaClassBase *inst,
+   const OSMetaClass *meta);
 
   OSMetaClassBase *metaCast(const char *toMeta);
 
Index: cfe/trunk/test/Analysis/osobject-retain-release.cpp
===
--- cfe/trunk/test/Analysis/osobject-retain-release.cpp
+++ cfe/trunk/test/Analysis/osobject-retain-release.cpp
@@ -1,9 +1,11 @@
 // RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\
-// RUN:-analyzer-checker=core,osx -verify %s
+// RUN:   -analyzer-checker=core,osx,debug.ExprInspection -verify %s
 
 #include "os_object_base.h"
 #include "os_smart_ptr.h"
 
+void clang_analyzer_eval(bool);
+
 struct OSIterator : public OSObject {
   static const OSMetaClass * const metaClass;
 };
@@ -483,6 +485,23 @@
   arr->release();
 }
 
+void check_required_cast() {
+  OSArray *arr = OSRequiredCast(OSArray, OSObject::generateObject(1));
+  arr->release(); // no-warning
+}
+
+void check_cast_behavior(OSObject *obj) {
+  OSArray *arr1 = OSDynamicCast(OSArray, obj);
+  clang_analyzer_eval(arr1 == obj); // expected-warning{{TRUE}}
+// expected-note@-1{{TRUE}}
+// expected-note@-2{{Assuming 'arr1' is 
not equal to 'obj'}}
+// expected-warning@-3{{FALSE}}
+// expected-note@-4   {{FALSE}}
+  OSArray *arr2 = OSRequiredCast(OSArray, obj);
+  clang_analyzer_eval(arr2 == obj); // expected-warning{{TRUE}}
+// expected-note@-1{{TRUE}}
+}
+
 unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) {
   OSArray *arr = OSDynamicCast(OSArray, obj);
   if (arr) {


Index: cfe/trunk/lib/Analysis/RetainSummaryManager.cpp
===
--- cfe/trunk/lib/Analysis/RetainSummaryManager.cpp
+++ cfe/trunk/lib/Analysis/RetainSummaryManager.cpp
@@ -152,6 +152,10 @@
   return S == "safeMetaCast";
 }
 
+static bool isOSObjectRequiredCast(StringRef S) {
+  return S == "requiredMetaCast";
+}
+
 static bool isOSObjectThisCast(StringRef S) {
   

[PATCH] D63117: [analyzer] RetainCount: Add support for OSRequiredCast().

2019-06-10 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added a reviewer: dcoughlin.
Herald added subscribers: cfe-commits, Charusso, dkrupp, donat.nagy, Szelethus, 
mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, xazax.hun.
Herald added a project: clang.

It's a new API for custom RTTI in Apple IOKit/DriverKit framework that is 
similar to `OSDynamicCast()` that's already supported, but crashes with 
assertion instead of returning null (and therefore causing UB when the cast 
fails unexpectedly). Kinda like `cast_or_null<>` as opposed to 
`dyn_cast_or_null<>` in LLVM's RTTI.

Historically, `RetainCountChecker` is responsible for modeling `OSDynamicCast`, 
so i simply extend this functionality.


Repository:
  rC Clang

https://reviews.llvm.org/D63117

Files:
  clang/lib/Analysis/RetainSummaryManager.cpp
  clang/test/Analysis/os_object_base.h
  clang/test/Analysis/osobject-retain-release.cpp


Index: clang/test/Analysis/osobject-retain-release.cpp
===
--- clang/test/Analysis/osobject-retain-release.cpp
+++ clang/test/Analysis/osobject-retain-release.cpp
@@ -1,9 +1,11 @@
 // RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\
-// RUN:-analyzer-checker=core,osx -verify %s
+// RUN:   -analyzer-checker=core,osx,debug.ExprInspection -verify %s
 
 #include "os_object_base.h"
 #include "os_smart_ptr.h"
 
+void clang_analyzer_eval(bool);
+
 struct OSIterator : public OSObject {
   static const OSMetaClass * const metaClass;
 };
@@ -483,6 +485,23 @@
   arr->release();
 }
 
+void check_required_cast() {
+  OSArray *arr = OSRequiredCast(OSArray, OSObject::generateObject(1));
+  arr->release(); // no-warning
+}
+
+void check_cast_behavior(OSObject *obj) {
+  OSArray *arr1 = OSDynamicCast(OSArray, obj);
+  clang_analyzer_eval(arr1 == obj); // expected-warning{{TRUE}}
+// expected-note@-1{{TRUE}}
+// expected-note@-2{{Assuming 'arr1' is 
not equal to 'obj'}}
+// expected-warning@-3{{FALSE}}
+// expected-note@-4   {{FALSE}}
+  OSArray *arr2 = OSRequiredCast(OSArray, obj);
+  clang_analyzer_eval(arr2 == obj); // expected-warning{{TRUE}}
+// expected-note@-1{{TRUE}}
+}
+
 unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) {
   OSArray *arr = OSDynamicCast(OSArray, obj);
   if (arr) {
Index: clang/test/Analysis/os_object_base.h
===
--- clang/test/Analysis/os_object_base.h
+++ clang/test/Analysis/os_object_base.h
@@ -12,6 +12,8 @@
 
 #define OSDynamicCast(type, inst)   \
 ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type)))
+#define OSRequiredCast(type, inst)   \
+((type *) OSMetaClassBase::requiredMetaCast((inst), OSTypeID(type)))
 
 #define OSTypeAlloc(type)   ((type *) ((type::metaClass)->alloc()))
 
@@ -22,6 +24,8 @@
 struct OSMetaClassBase {
   static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst,
const OSMetaClass *meta);
+  static OSMetaClassBase *requiredMetaCast(const OSMetaClassBase *inst,
+   const OSMetaClass *meta);
 
   OSMetaClassBase *metaCast(const char *toMeta);
 
Index: clang/lib/Analysis/RetainSummaryManager.cpp
===
--- clang/lib/Analysis/RetainSummaryManager.cpp
+++ clang/lib/Analysis/RetainSummaryManager.cpp
@@ -152,6 +152,10 @@
   return S == "safeMetaCast";
 }
 
+static bool isOSObjectRequiredCast(StringRef S) {
+  return S == "requiredMetaCast";
+}
+
 static bool isOSObjectThisCast(StringRef S) {
   return S == "metaCast";
 }
@@ -234,7 +238,8 @@
   if (RetTy->isPointerType()) {
 const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
 if (PD && isOSObjectSubclass(PD)) {
-  if (isOSObjectDynamicCast(FName) || isOSObjectThisCast(FName))
+  if (isOSObjectDynamicCast(FName) || isOSObjectRequiredCast(FName) ||
+  isOSObjectThisCast(FName))
 return getDefaultSummary();
 
   // TODO: Add support for the slightly common *Matching(table) idiom.
@@ -745,6 +750,8 @@
 if (TrackOSObjects) {
   if (isOSObjectDynamicCast(FName) && FD->param_size() >= 1) {
 return BehaviorSummary::IdentityOrZero;
+  } else if (isOSObjectRequiredCast(FName) && FD->param_size() >= 1) {
+return BehaviorSummary::Identity;
   } else if (isOSObjectThisCast(FName) && isa(FD) &&
  !cast(FD)->isStatic()) {
 return BehaviorSummary::IdentityThis;


Index: clang/test/Analysis/osobject-retain-release.cpp
===
--- clang/test/Analysis/osobject-retain-release.cpp
+++ clang/test/Analysis/osobject-retain-release.cpp
@@ -1,9 +1,11 @@
 // RUN: %clang_analyze_cc1 -fblocks -analyze