Summary:public Create a model of std::unique_ptr in similar fashion to what was done to std::shared_ptr. For now, we are modeling it as container of raw pointer (no ownership concept). This time unique_ptr is not derived from std__unique_ptr (unlike shared_ptr, it was easier to not do that) and so we need to provide implementations for all non-member functions per C++ reference: http://en.cppreference.com/w/cpp/memory/unique_ptr Reviewed By: dulmarod Differential Revision: D3048209 fb-gh-sync-id: a9a6455 shipit-source-id: a9a6455master
parent
9f7bfea98f
commit
675009a2ee
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - present Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
// this file exists in gcc headers and we need to capture those includes
|
||||
#include <infer_model/begin_name_override.inc>
|
||||
#include_next <backward/auto_ptr.h>
|
||||
#include <infer_model/end_name_override.inc>
|
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - present Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
// this file exists in gcc headers and we need to capture those includes
|
||||
#include <infer_model/begin_name_override.inc>
|
||||
#include_next <bits/unique_ptr.h>
|
||||
#include <infer_model/end_name_override.inc>
|
||||
|
||||
#include <infer_model/unique_ptr.h>
|
@ -1,3 +1,4 @@
|
||||
#define make_shared std__make_shared
|
||||
#define shared_ptr std__shared_ptr
|
||||
|
||||
#define unique_ptr std__unique_ptr
|
||||
#define make_unique std__make_unique
|
||||
|
@ -1,2 +1,4 @@
|
||||
#undef make_shared
|
||||
#undef shared_ptr
|
||||
#undef unique_ptr
|
||||
#undef make_unique
|
||||
|
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - present Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <infer_model/common.h>
|
||||
|
||||
INFER_NAMESPACE_STD_BEGIN
|
||||
|
||||
// IMPORTANT
|
||||
// There is specialization of unique_ptr below and it's mosly copy paste.
|
||||
// When changing model, remember to change it for specialization as well!
|
||||
template <class _Tp, class _Dp = default_delete<_Tp>>
|
||||
struct unique_ptr {
|
||||
// use SFINAE to determine whether _Del::pointer exists
|
||||
class _Pointer {
|
||||
template <typename _Up>
|
||||
static typename _Up::pointer __test(typename _Up::pointer*);
|
||||
|
||||
template <typename _Up>
|
||||
static _Tp* __test(...);
|
||||
|
||||
typedef typename remove_reference<_Dp>::type _Del;
|
||||
|
||||
public:
|
||||
typedef decltype(__test<_Del>(0)) type;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef typename _Pointer::type pointer;
|
||||
typedef _Tp element_type;
|
||||
typedef _Dp deleter_type;
|
||||
|
||||
pointer data;
|
||||
template <class Y>
|
||||
unique_ptr(const std__unique_ptr<Y>& u) {}
|
||||
|
||||
constexpr unique_ptr() noexcept : data(nullptr) {}
|
||||
|
||||
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr<_Tp, _Dp>() {}
|
||||
|
||||
unique_ptr(pointer ptr) : data(ptr) {}
|
||||
|
||||
unique_ptr(pointer ptr,
|
||||
typename conditional<
|
||||
is_reference<deleter_type>::value,
|
||||
deleter_type,
|
||||
typename add_lvalue_reference<const deleter_type>::type>::type
|
||||
__d) noexcept : data(ptr) {}
|
||||
|
||||
unique_ptr(pointer ptr,
|
||||
typename remove_reference<deleter_type>::type&& __d) noexcept
|
||||
: data(ptr) {}
|
||||
|
||||
unique_ptr(unique_ptr&& u) noexcept : data(u.data) { u.data = nullptr; }
|
||||
|
||||
template <class _Up,
|
||||
class _Ep,
|
||||
typename = typename enable_if<
|
||||
!is_array<_Up>::value &&
|
||||
is_convertible<typename unique_ptr<_Up, _Ep>::pointer,
|
||||
pointer>::value &&
|
||||
is_convertible<_Ep, deleter_type>::value &&
|
||||
(!is_reference<deleter_type>::value ||
|
||||
is_same<deleter_type, _Ep>::value)>::type>
|
||||
unique_ptr(unique_ptr<_Up, _Ep>&& u) noexcept : data(u.data) {
|
||||
u.data = nullptr;
|
||||
}
|
||||
|
||||
template <
|
||||
class _Up,
|
||||
typename = typename enable_if<is_convertible<_Up*, _Tp*>::value>::type>
|
||||
unique_ptr(auto_ptr<_Up>&& __p) noexcept;
|
||||
|
||||
~unique_ptr() { reset(); }
|
||||
|
||||
unique_ptr& operator=(unique_ptr&& __u) noexcept {
|
||||
reset(__u.data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class _Up,
|
||||
class _Ep,
|
||||
typename = typename enable_if<
|
||||
!is_array<_Up>::value &&
|
||||
is_convertible<typename unique_ptr<_Up, _Ep>::pointer,
|
||||
pointer>::value &&
|
||||
is_assignable<deleter_type&, _Ep&&>::value>::type>
|
||||
unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) noexcept {
|
||||
reset(__u.data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
unique_ptr& operator=(nullptr_t) noexcept {
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
typename add_lvalue_reference<_Tp>::type operator*() const { return *data; }
|
||||
|
||||
pointer operator->() const { return data; }
|
||||
|
||||
pointer get() const { return data; }
|
||||
|
||||
typedef typename remove_reference<deleter_type>::type& _Dp_reference;
|
||||
typedef const typename remove_reference<deleter_type>::type&
|
||||
_Dp_const_reference;
|
||||
_Dp_const_reference get_deleter() const {}
|
||||
_Dp_reference get_deleter() {}
|
||||
|
||||
explicit operator bool() const { return data != nullptr; }
|
||||
pointer release() { return data; }
|
||||
|
||||
void reset(pointer p = nullptr) { data = p; }
|
||||
|
||||
void swap(unique_ptr& u) {
|
||||
pointer tmp = data;
|
||||
data = u.data;
|
||||
u.data = tmp;
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Tp, class _Dp>
|
||||
struct unique_ptr<_Tp[], _Dp> {
|
||||
// use SFINAE to determine whether _Del::pointer exists
|
||||
class _Pointer {
|
||||
template <typename _Up>
|
||||
static typename _Up::pointer __test(typename _Up::pointer*);
|
||||
|
||||
template <typename _Up>
|
||||
static _Tp* __test(...);
|
||||
|
||||
typedef typename remove_reference<_Dp>::type _Del;
|
||||
|
||||
public:
|
||||
typedef decltype(__test<_Del>(0)) type;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef typename _Pointer::type pointer;
|
||||
typedef _Tp element_type;
|
||||
typedef _Dp deleter_type;
|
||||
|
||||
pointer data;
|
||||
|
||||
constexpr unique_ptr() noexcept : data(nullptr) {}
|
||||
|
||||
constexpr unique_ptr(nullptr_t) noexcept : data(nullptr) {}
|
||||
|
||||
unique_ptr(pointer ptr) : data(ptr) {}
|
||||
|
||||
unique_ptr(
|
||||
pointer ptr,
|
||||
typename conditional<
|
||||
is_reference<deleter_type>::value,
|
||||
deleter_type,
|
||||
typename add_lvalue_reference<const deleter_type>::type>::type __d)
|
||||
: data(ptr) {}
|
||||
|
||||
unique_ptr(pointer ptr, typename remove_reference<deleter_type>::type&& __d)
|
||||
: data(ptr) {}
|
||||
|
||||
unique_ptr(unique_ptr&& u) : data(u.data) { u.data = nullptr; }
|
||||
|
||||
template <class _Up,
|
||||
class _Ep,
|
||||
typename = typename enable_if<
|
||||
is_array<_Up>::value &&
|
||||
is_convertible<typename unique_ptr<_Up, _Ep>::pointer,
|
||||
pointer>::value &&
|
||||
is_convertible<_Ep, deleter_type>::value &&
|
||||
(!is_reference<deleter_type>::value ||
|
||||
is_same<deleter_type, _Ep>::value)>::type>
|
||||
unique_ptr(unique_ptr<_Up, _Ep>&& u) : data(u.data) {
|
||||
u.data = nullptr;
|
||||
}
|
||||
|
||||
template <
|
||||
class _Up,
|
||||
typename = typename enable_if<is_convertible<_Up*, _Tp*>::value>::type>
|
||||
unique_ptr(auto_ptr<_Up>&& __p) noexcept;
|
||||
|
||||
~unique_ptr() { reset(); }
|
||||
|
||||
unique_ptr& operator=(unique_ptr&& __u) {
|
||||
reset(__u.data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class _Up,
|
||||
class _Ep,
|
||||
typename = typename enable_if<
|
||||
is_array<_Up>::value &&
|
||||
is_convertible<typename unique_ptr<_Up, _Ep>::pointer,
|
||||
pointer>::value &&
|
||||
is_assignable<deleter_type&, _Ep&&>::value>::type>
|
||||
unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) {
|
||||
reset(__u.data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
unique_ptr& operator=(nullptr_t) {
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
typename add_lvalue_reference<_Tp>::type operator[](size_t i) const {}
|
||||
|
||||
pointer get() const { return data; }
|
||||
|
||||
typedef typename remove_reference<deleter_type>::type& _Dp_reference;
|
||||
typedef const typename remove_reference<deleter_type>::type&
|
||||
_Dp_const_reference;
|
||||
_Dp_const_reference get_deleter() const {}
|
||||
_Dp_reference get_deleter() {}
|
||||
|
||||
explicit operator bool() const { return data != nullptr; }
|
||||
pointer release() { return data; }
|
||||
|
||||
void reset(pointer p = nullptr) { data = p; }
|
||||
|
||||
void swap(unique_ptr& u) {
|
||||
pointer tmp = data;
|
||||
data = u.data;
|
||||
u.data = tmp;
|
||||
}
|
||||
};
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
inline bool operator==(const unique_ptr<_T1, _D1>& __x,
|
||||
const unique_ptr<_T2, _D2>& __y) {
|
||||
return __x.get() == __y.get();
|
||||
}
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
inline bool operator!=(const unique_ptr<_T1, _D1>& __x,
|
||||
const unique_ptr<_T2, _D2>& __y) {
|
||||
return !(__x == __y);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
inline bool operator<(const unique_ptr<_T1, _D1>& __x,
|
||||
const unique_ptr<_T2, _D2>& __y) {
|
||||
/*typedef typename unique_ptr<_T1, _D1>::pointer _P1;
|
||||
typedef typename unique_ptr<_T2, _D2>::pointer _P2;
|
||||
typedef typename common_type<_P1, _P2>::type _Vp;
|
||||
return less<_Vp>()(__x.get(), __y.get());*/
|
||||
}
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
inline bool operator>(const unique_ptr<_T1, _D1>& __x,
|
||||
const unique_ptr<_T2, _D2>& __y) {
|
||||
return __y < __x;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
inline bool operator<=(const unique_ptr<_T1, _D1>& __x,
|
||||
const unique_ptr<_T2, _D2>& __y) {
|
||||
return !(__y < __x);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
inline bool operator>=(const unique_ptr<_T1, _D1>& __x,
|
||||
const unique_ptr<_T2, _D2>& __y) {
|
||||
return !(__x < __y);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) {
|
||||
return !__x;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator==(nullptr_t, const unique_ptr<_T1, _D1>& __x) {
|
||||
return !__x;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator!=(const unique_ptr<_T1, _D1>& __x, nullptr_t) {
|
||||
return static_cast<bool>(__x);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator!=(nullptr_t, const unique_ptr<_T1, _D1>& __x) {
|
||||
return static_cast<bool>(__x);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator<(const unique_ptr<_T1, _D1>& __x, nullptr_t) {
|
||||
/*typedef typename unique_ptr<_T1, _D1>::pointer _P1;
|
||||
return less<_P1>()(__x.get(), nullptr);*/
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator<(nullptr_t, const unique_ptr<_T1, _D1>& __x) {
|
||||
/*typedef typename unique_ptr<_T1, _D1>::pointer _P1;
|
||||
return less<_P1>()(nullptr, __x.get());*/
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator>(const unique_ptr<_T1, _D1>& __x, nullptr_t) {
|
||||
return nullptr < __x;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator>(nullptr_t, const unique_ptr<_T1, _D1>& __x) {
|
||||
return __x < nullptr;
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator<=(const unique_ptr<_T1, _D1>& __x, nullptr_t) {
|
||||
return !(nullptr < __x);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator<=(nullptr_t, const unique_ptr<_T1, _D1>& __x) {
|
||||
return !(__x < nullptr);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator>=(const unique_ptr<_T1, _D1>& __x, nullptr_t) {
|
||||
return !(__x < nullptr);
|
||||
}
|
||||
|
||||
template <class _T1, class _D1>
|
||||
inline bool operator>=(nullptr_t, const unique_ptr<_T1, _D1>& __x) {
|
||||
return !(nullptr < __x);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct hash<unique_ptr<T>> : public hash<std__unique_ptr<T>> {};
|
||||
|
||||
template <typename _Tp>
|
||||
struct _MakeUniq2 {
|
||||
typedef unique_ptr<_Tp> __single_object;
|
||||
};
|
||||
|
||||
template <typename _Tp>
|
||||
struct _MakeUniq2<_Tp[]> {
|
||||
typedef unique_ptr<_Tp[]> __array;
|
||||
};
|
||||
|
||||
template <typename _Tp, size_t _Bound>
|
||||
struct _MakeUniq2<_Tp[_Bound]> {
|
||||
struct __invalid_type {};
|
||||
};
|
||||
|
||||
/// std::make_unique for single objects
|
||||
template <typename _Tp, typename... _Args>
|
||||
inline typename _MakeUniq2<_Tp>::__single_object make_unique(
|
||||
_Args&&... __args) {
|
||||
return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...));
|
||||
}
|
||||
|
||||
/// std::make_unique for arrays of unknown bound
|
||||
template <typename _Tp>
|
||||
inline typename _MakeUniq2<_Tp>::__array make_unique(size_t __num) {
|
||||
return unique_ptr<_Tp>(new typename remove_extent<_Tp>::type[__num]());
|
||||
}
|
||||
|
||||
/// Disable std::make_unique for arrays of known bound
|
||||
template <typename _Tp, typename... _Args>
|
||||
inline typename _MakeUniq2<_Tp>::__invalid_type make_unique(_Args&&...) =
|
||||
delete;
|
||||
INFER_NAMESPACE_STD_END
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - present Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct X {
|
||||
int field;
|
||||
int get() { return field; }
|
||||
void set(int value) { field = value; }
|
||||
};
|
||||
|
||||
int empty_ptr_access() {
|
||||
std::unique_ptr<int> x;
|
||||
int* p = x.get(); // no dereference
|
||||
if (p) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int empty_ptr_deref() {
|
||||
std::unique_ptr<int> x;
|
||||
return *x;
|
||||
}
|
||||
|
||||
int nullptr_ptr_deref() {
|
||||
std::unique_ptr<int> x(nullptr);
|
||||
return *x;
|
||||
}
|
||||
|
||||
int empty_ptr_field_deref() {
|
||||
std::unique_ptr<X> x;
|
||||
return x.get()->field;
|
||||
}
|
||||
|
||||
int empty_ptr_field_deref2() {
|
||||
std::unique_ptr<X> x;
|
||||
return x->field;
|
||||
}
|
||||
|
||||
int empty_ptr_method_deref() {
|
||||
std::unique_ptr<X> x;
|
||||
return x->get();
|
||||
}
|
||||
|
||||
int reset_ptr_null_deref() {
|
||||
std::unique_ptr<int> x(new int);
|
||||
x.reset();
|
||||
return *x;
|
||||
}
|
||||
|
||||
int reset_ptr_null_deref2() {
|
||||
std::unique_ptr<int> x(new int);
|
||||
x.reset(new int);
|
||||
x.reset();
|
||||
return *x;
|
||||
}
|
||||
|
||||
int reset_ptr_ok_deref() {
|
||||
std::unique_ptr<int> x;
|
||||
x.reset(new int);
|
||||
return *x;
|
||||
}
|
||||
|
||||
int reset_ptr_ok_deref2() {
|
||||
std::unique_ptr<int> x;
|
||||
x.reset();
|
||||
x.reset(new int);
|
||||
return *x;
|
||||
}
|
||||
|
||||
int unique_ptr_copy_null_deref() {
|
||||
std::unique_ptr<int> p1;
|
||||
std::unique_ptr<int> p2 = std::move(p1);
|
||||
return *p2;
|
||||
}
|
||||
|
||||
int unique_ptr_assign_null_deref() {
|
||||
std::unique_ptr<int> p1(new int);
|
||||
std::unique_ptr<int> p2;
|
||||
p1 = std::move(p2);
|
||||
return *p1;
|
||||
}
|
||||
|
||||
int unique_ptr_move_ok_deref() {
|
||||
std::unique_ptr<int> p1(new int);
|
||||
std::unique_ptr<int> p2 = std::move(p1);
|
||||
return *p2;
|
||||
}
|
||||
|
||||
int unique_ptr_assign_ok_deref() {
|
||||
std::unique_ptr<int> p1(new int);
|
||||
std::unique_ptr<int> p2;
|
||||
p2 = std::move(p1);
|
||||
p1.reset();
|
||||
return *p2;
|
||||
}
|
||||
|
||||
int unique_ptr_move_null_deref() {
|
||||
std::unique_ptr<int> p1(new int);
|
||||
std::unique_ptr<int> p2 = std::move(p1);
|
||||
return *p1;
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - present Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package endtoend.cpp;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static utils.matchers.ResultContainsExactly.containsExactly;
|
||||
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import utils.DebuggableTemporaryFolder;
|
||||
import utils.InferException;
|
||||
import utils.InferResults;
|
||||
import utils.InferRunner;
|
||||
|
||||
public class UniquePtrDerefTest {
|
||||
|
||||
public static final String FILE =
|
||||
"infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp";
|
||||
|
||||
private static ImmutableList<String> inferCmd;
|
||||
|
||||
public static final String NULL_DEREFERENCE = "NULL_DEREFERENCE";
|
||||
|
||||
@ClassRule
|
||||
public static DebuggableTemporaryFolder folder =
|
||||
new DebuggableTemporaryFolder();
|
||||
|
||||
@BeforeClass
|
||||
public static void runInfer() throws InterruptedException, IOException {
|
||||
inferCmd = InferRunner.createCPPInferCommand(folder, FILE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenInferRunsNullDerefFunctionsErrorIsFound()
|
||||
throws InterruptedException, IOException, InferException {
|
||||
String[] procedures = {
|
||||
"empty_ptr_deref",
|
||||
"nullptr_ptr_deref",
|
||||
"empty_ptr_field_deref",
|
||||
"empty_ptr_field_deref2",
|
||||
"empty_ptr_method_deref",
|
||||
"reset_ptr_null_deref",
|
||||
"reset_ptr_null_deref2",
|
||||
"unique_ptr_copy_null_deref",
|
||||
"unique_ptr_assign_null_deref",
|
||||
"unique_ptr_move_null_deref",
|
||||
};
|
||||
InferResults inferResults = InferRunner.runInferCPP(inferCmd);
|
||||
assertThat(
|
||||
"Results should contain null dereference",
|
||||
inferResults,
|
||||
containsExactly(
|
||||
NULL_DEREFERENCE,
|
||||
FILE,
|
||||
procedures
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue