From 4637bf877edd1636c418dbb375eac95768e228fa Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Wed, 31 Aug 2016 06:42:28 -0700 Subject: [PATCH] model unique_ptr as T* Summary: Follow strategy that was done to `std::shared_ptr` model and translate `std::unique_ptr` as raw pointer `T*`. As a bonus, model `operator[]` of array overload as dereference Reviewed By: jvillard Differential Revision: D3785031 fbshipit-source-id: 2c5b0a4 --- .../cpp/include/infer_model/infer_traits.h | 2 + .../cpp/include/infer_model/unique_ptr.h | 161 +++++++++++++----- .../cpp/errors/smart_ptr/unique_ptr_deref.cpp | 10 ++ .../cpp/infer/UniquePtrDerefTest.java | 2 + 4 files changed, 136 insertions(+), 39 deletions(-) diff --git a/infer/models/cpp/include/infer_model/infer_traits.h b/infer/models/cpp/include/infer_model/infer_traits.h index eb6762ba1..0062fa090 100644 --- a/infer/models/cpp/include/infer_model/infer_traits.h +++ b/infer/models/cpp/include/infer_model/infer_traits.h @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#pragma once + namespace infer_traits { // all friends of this class and will be translated as type T by infer // frontend instead of their own type diff --git a/infer/models/cpp/include/infer_model/unique_ptr.h b/infer/models/cpp/include/infer_model/unique_ptr.h index 6464cc6b2..377c3781c 100644 --- a/infer/models/cpp/include/infer_model/unique_ptr.h +++ b/infer/models/cpp/include/infer_model/unique_ptr.h @@ -10,6 +10,7 @@ #pragma once #include +#include INFER_NAMESPACE_STD_BEGIN @@ -18,6 +19,7 @@ INFER_NAMESPACE_STD_BEGIN // When changing model, remember to change it for specialization as well! template > struct unique_ptr { + // use SFINAE to determine whether _Del::pointer exists class _Pointer { template @@ -31,34 +33,72 @@ struct unique_ptr { public: typedef decltype(__test<_Del>(0)) type; }; - public: typedef typename _Pointer::type pointer; typedef _Tp element_type; typedef _Dp deleter_type; - pointer data; + private: + /* std::unique_ptr in infer is translated as T* + Look at model of std::shared_ptr for more details */ + + // translate shared_ptr as type 'pointer' + friend class infer_traits::TranslateAsType; + + /// type of 'this' in unique_ptr as seen by infer + typedef const void** infer_unique_ptr_t; +// use it to avoid compilation errors and make infer analyzer happy +#define __cast_to_infer_ptr(self) ((infer_unique_ptr_t)self) + + static void model_set(infer_unique_ptr_t self, const void* value) { + *self = value; + } + + static void model_move(infer_unique_ptr_t self, infer_unique_ptr_t other) { + *self = *other; + model_set(other, nullptr); + } + + static pointer model_get(infer_unique_ptr_t self) { return (pointer)(*self); } + + static void model_swap(infer_unique_ptr_t infer_self, + infer_unique_ptr_t infer_other) { + const void* t = *infer_self; + *infer_self = *infer_other; + *infer_other = t; + } + + pointer __ignore__; // used to keep sizeof(unique_ptr) same as in standard + + public: template unique_ptr(const std__unique_ptr& u) {} - constexpr unique_ptr() noexcept : data(nullptr) {} + constexpr unique_ptr() noexcept { + model_set(__cast_to_infer_ptr(this), nullptr); + } constexpr unique_ptr(nullptr_t) noexcept : unique_ptr<_Tp, _Dp>() {} - explicit unique_ptr(pointer ptr) : data(ptr) {} + explicit unique_ptr(pointer ptr) { + model_set(__cast_to_infer_ptr(this), ptr); + } unique_ptr(pointer ptr, typename conditional< is_reference::value, deleter_type, typename add_lvalue_reference::type>::type - __d) noexcept : data(ptr) {} + __d) noexcept + : unique_ptr<_Tp, _Dp>(ptr) {} unique_ptr(pointer ptr, typename remove_reference::type&& __d) noexcept - : data(ptr) {} + : unique_ptr<_Tp, _Dp>(ptr) {} - unique_ptr(unique_ptr&& u) noexcept : data(u.data) { u.data = nullptr; } + unique_ptr(unique_ptr&& u) noexcept { + model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&u)); + } template ::value && (!is_reference::value || is_same::value)>::type> - unique_ptr(unique_ptr<_Up, _Ep>&& u) noexcept : data(u.data) { - u.data = nullptr; + unique_ptr(unique_ptr<_Up, _Ep>&& u) noexcept { + model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&u)); } template < @@ -81,7 +121,7 @@ struct unique_ptr { ~unique_ptr() { reset(); } unique_ptr& operator=(unique_ptr&& __u) noexcept { - reset(__u.data); + model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&__u)); return *this; } @@ -93,7 +133,7 @@ struct unique_ptr { pointer>::value && is_assignable::value>::type> unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) noexcept { - reset(__u.data); + model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&__u)); return *this; } @@ -101,11 +141,14 @@ struct unique_ptr { reset(); return *this; } - typename add_lvalue_reference<_Tp>::type operator*() const { return *data; } + typename add_lvalue_reference<_Tp>::type operator*() const + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} - pointer operator->() const { return data; } + pointer operator->() const + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} - pointer get() const { return data; } + pointer get() const + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} typedef typename remove_reference::type& _Dp_reference; typedef const typename remove_reference::type& @@ -113,15 +156,16 @@ struct unique_ptr { _Dp_const_reference get_deleter() const {} _Dp_reference get_deleter() {} - explicit operator bool() const { return data != nullptr; } - pointer release() { return data; } + explicit operator bool() const { + return !!(bool)(model_get(__cast_to_infer_ptr(this))); + } + pointer release() + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} - void reset(pointer p = nullptr) { data = p; } + void reset(pointer p = nullptr) { model_set(__cast_to_infer_ptr(this), p); } void swap(unique_ptr& u) { - pointer tmp = data; - data = u.data; - u.data = tmp; + model_swap(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&u)); } }; @@ -146,13 +190,45 @@ struct unique_ptr<_Tp[], _Dp> { typedef _Tp element_type; typedef _Dp deleter_type; - pointer data; + private: + // translate shared_ptr as type pointer + friend class infer_traits::TranslateAsType; + + /// type of 'this' in unique_ptr as seen by infer + typedef const void** infer_unique_ptr_t; +// use it to avoid compilation errors and make infer analyzer happy +#define __cast_to_infer_ptr(self) ((infer_unique_ptr_t)self) + + static void model_set(infer_unique_ptr_t self, const void* value) { + *self = value; + } + + static void model_move(infer_unique_ptr_t self, infer_unique_ptr_t other) { + *self = *other; + model_set(other, nullptr); + } + + static pointer model_get(infer_unique_ptr_t self) { return (pointer)(*self); } + + static void model_swap(infer_unique_ptr_t infer_self, + infer_unique_ptr_t infer_other) { + const void* t = *infer_self; + *infer_self = *infer_other; + *infer_other = t; + } + + pointer __ignore__; // used to keep sizeof(unique_ptr) same as in standard - constexpr unique_ptr() noexcept : data(nullptr) {} + public: + constexpr unique_ptr() noexcept { + model_set(__cast_to_infer_ptr(this), nullptr); + } - constexpr unique_ptr(nullptr_t) noexcept : data(nullptr) {} + constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() {} - explicit unique_ptr(pointer ptr) : data(ptr) {} + explicit unique_ptr(pointer ptr) { + model_set(__cast_to_infer_ptr(this), ptr); + } unique_ptr( pointer ptr, @@ -160,12 +236,14 @@ struct unique_ptr<_Tp[], _Dp> { is_reference::value, deleter_type, typename add_lvalue_reference::type>::type __d) - : data(ptr) {} + : unique_ptr(ptr) {} unique_ptr(pointer ptr, typename remove_reference::type&& __d) - : data(ptr) {} + : unique_ptr(ptr) {} - unique_ptr(unique_ptr&& u) : data(u.data) { u.data = nullptr; } + unique_ptr(unique_ptr&& u) { + model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&u)); + } template { is_convertible<_Ep, deleter_type>::value && (!is_reference::value || is_same::value)>::type> - unique_ptr(unique_ptr<_Up, _Ep>&& u) : data(u.data) { - u.data = nullptr; + unique_ptr(unique_ptr<_Up, _Ep>&& u) { + model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&u)); } template < @@ -188,7 +266,7 @@ struct unique_ptr<_Tp[], _Dp> { ~unique_ptr() { reset(); } unique_ptr& operator=(unique_ptr&& __u) { - reset(__u.data); + model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&__u)); return *this; } @@ -200,7 +278,7 @@ struct unique_ptr<_Tp[], _Dp> { pointer>::value && is_assignable::value>::type> unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) { - reset(__u.data); + model_move(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&__u)); return *this; } @@ -209,9 +287,11 @@ struct unique_ptr<_Tp[], _Dp> { return *this; } - typename add_lvalue_reference<_Tp>::type operator[](size_t i) const {} + typename add_lvalue_reference<_Tp>::type operator[](size_t i) const + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} - pointer get() const { return data; } + pointer get() const + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} typedef typename remove_reference::type& _Dp_reference; typedef const typename remove_reference::type& @@ -219,15 +299,16 @@ struct unique_ptr<_Tp[], _Dp> { _Dp_const_reference get_deleter() const {} _Dp_reference get_deleter() {} - explicit operator bool() const { return data != nullptr; } - pointer release() { return data; } + explicit operator bool() const { + return !!(bool)(model_get(__cast_to_infer_ptr(this))); + } + pointer release() + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} - void reset(pointer p = nullptr) { data = p; } + void reset(pointer p = nullptr) { model_set(__cast_to_infer_ptr(this), p); } void swap(unique_ptr& u) { - pointer tmp = data; - data = u.data; - u.data = tmp; + model_swap(__cast_to_infer_ptr(this), __cast_to_infer_ptr(&u)); } }; @@ -370,3 +451,5 @@ template inline typename _MakeUniq2<_Tp>::__invalid_type make_unique(_Args&&...) = delete; INFER_NAMESPACE_STD_END + +#undef __cast_to_infer_ptr diff --git a/infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp b/infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp index a9943ab45..18d9e49ff 100644 --- a/infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp +++ b/infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp @@ -29,11 +29,21 @@ int empty_ptr_deref() { return *x; } +int empty_array_ptr_deref() { + std::unique_ptr x; + return x[0]; +} + int nullptr_ptr_deref() { std::unique_ptr x(nullptr); return *x; } +int nullptr_array_ptr_deref() { + std::unique_ptr x(nullptr); + return x[2]; +} + int empty_ptr_field_deref() { std::unique_ptr x; return x.get()->field; diff --git a/infer/tests/endtoend/cpp/infer/UniquePtrDerefTest.java b/infer/tests/endtoend/cpp/infer/UniquePtrDerefTest.java index 9c5630e42..e59453599 100644 --- a/infer/tests/endtoend/cpp/infer/UniquePtrDerefTest.java +++ b/infer/tests/endtoend/cpp/infer/UniquePtrDerefTest.java @@ -49,7 +49,9 @@ public class UniquePtrDerefTest { throws InterruptedException, IOException, InferException { String[] procedures = { "empty_ptr_deref", + "empty_array_ptr_deref", "nullptr_ptr_deref", + "nullptr_array_ptr_deref", "empty_ptr_field_deref", "empty_ptr_field_deref2", "empty_ptr_method_deref",