Summary: The time has come to keep track of which tests pass and which are FP/FN for pulse. Reviewed By: mbouaziz Differential Revision: D10854064 fbshipit-source-id: 60938e48fmaster
parent
cf66ea0afb
commit
6cce767d19
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct Aggregate {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
~Aggregate() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void FP_aggregate_reassign_ok() {
|
||||||
|
const int len = 5;
|
||||||
|
Aggregate arr[len];
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
Aggregate s = {1};
|
||||||
|
// assign with curly bracket syntax doesn't call constructor; need to
|
||||||
|
// recognize that this is a reassignment anyway
|
||||||
|
arr[0] = s; // shouldn't be flagged as a use-after-lifetime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AggregateWithConstructedField {
|
||||||
|
std::string str;
|
||||||
|
};
|
||||||
|
|
||||||
|
void FP_aggregate_reassign2_ok() {
|
||||||
|
AggregateWithConstructedField arr[10];
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
// this is translated as string(&(a.str), "hi"). need to make sure this is
|
||||||
|
// treated the same as initializing a
|
||||||
|
AggregateWithConstructedField a{"hi"};
|
||||||
|
arr[i] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NestedAggregate {
|
||||||
|
AggregateWithConstructedField a;
|
||||||
|
};
|
||||||
|
|
||||||
|
void FP_aggregate_reassign3_ok() {
|
||||||
|
NestedAggregate arr[10];
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
// this is translated as std::basic_string(&(a.str), "hi"). need to make
|
||||||
|
// sure this is treated the same as initializing a
|
||||||
|
NestedAggregate a{{"hi"}};
|
||||||
|
arr[i] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int multiple_invalidations_branch_bad(int n, int* ptr) {
|
||||||
|
if (n == 7) {
|
||||||
|
delete ptr;
|
||||||
|
} else {
|
||||||
|
delete ptr;
|
||||||
|
}
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int multiple_invalidations_loop_bad(int n, int* ptr) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (i == 7) {
|
||||||
|
delete ptr;
|
||||||
|
} else {
|
||||||
|
delete ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aggregate* FP_pointer_arithmetic_ok(Aggregate* a) {
|
||||||
|
a->~Aggregate();
|
||||||
|
a++;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iterator_pointer_arithmetic_ok(std::vector<Aggregate> v) {
|
||||||
|
for (auto it = v.begin(); it != v.end(); ++it) {
|
||||||
|
it->~Aggregate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct A {
|
||||||
|
~A();
|
||||||
|
int f(int i);
|
||||||
|
};
|
||||||
|
|
||||||
|
A getA();
|
||||||
|
|
||||||
|
int FP_struct_inside_loop_ok(std::vector<int> numbers) {
|
||||||
|
int sum;
|
||||||
|
for (auto number : numbers) {
|
||||||
|
A a = getA();
|
||||||
|
sum += a.f(number);
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
int f;
|
||||||
|
|
||||||
|
S() { f = 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
int FN_ref_capture_destroy_invoke_bad() {
|
||||||
|
std::function<int()> f;
|
||||||
|
{
|
||||||
|
S s;
|
||||||
|
f = [&s] { return s.f; };
|
||||||
|
} // destructor for s called here
|
||||||
|
return f(); // s used here
|
||||||
|
}
|
||||||
|
|
||||||
|
int FN_implicit_ref_capture_destroy_invoke_bad() {
|
||||||
|
std::function<int()> f;
|
||||||
|
{
|
||||||
|
auto s = S();
|
||||||
|
f = [&] { return s.f; };
|
||||||
|
}
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
|
||||||
|
int FN_reassign_lambda_capture_destroy_invoke_bad() {
|
||||||
|
std::function<int()> f;
|
||||||
|
{
|
||||||
|
auto s = S();
|
||||||
|
auto tmp = [&] { return s.f; };
|
||||||
|
f = tmp;
|
||||||
|
}
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
|
||||||
|
// frontend doesn't understand difference between capture-by-value and
|
||||||
|
// capture-by-ref, need to fix
|
||||||
|
int value_capture_destroy_invoke_ok() {
|
||||||
|
std::function<int()> f;
|
||||||
|
{
|
||||||
|
S s;
|
||||||
|
f = [s] { return s.f; };
|
||||||
|
}
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
|
||||||
|
// same thing here
|
||||||
|
int implicit_value_capture_destroy_invoke_ok() {
|
||||||
|
std::function<int()> f;
|
||||||
|
{
|
||||||
|
S s;
|
||||||
|
f = [=] { return s.f; };
|
||||||
|
}
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ref_capture_invoke_ok() {
|
||||||
|
std::function<int()> f;
|
||||||
|
int ret;
|
||||||
|
{
|
||||||
|
S s;
|
||||||
|
f = [&s] { return s.f; };
|
||||||
|
ret = f();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke_twice_ok() {
|
||||||
|
std::function<int()> f;
|
||||||
|
int ret;
|
||||||
|
{
|
||||||
|
S s;
|
||||||
|
f = [&s] { return s.f; };
|
||||||
|
f();
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<int()> ref_capture_read_lambda_ok() {
|
||||||
|
std::function<int()> f;
|
||||||
|
int ret;
|
||||||
|
{
|
||||||
|
S s;
|
||||||
|
f = [&s] { return s.f; };
|
||||||
|
}
|
||||||
|
auto tmp =
|
||||||
|
f; // reading (but not invoking) the lambda doesn't use its captured vars
|
||||||
|
}
|
||||||
|
|
||||||
|
// we'll miss this because we count invoking a lambda object as a use of its
|
||||||
|
// captured vars, not the lambda object itself.
|
||||||
|
void FN_delete_lambda_then_call_bad() {
|
||||||
|
auto lambda = [] { return 1; };
|
||||||
|
~lambda();
|
||||||
|
return lambda();
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to treat escaping as a use in order to catch this
|
||||||
|
std::function<int()> FN_ref_capture_return_lambda_bad() {
|
||||||
|
std::function<int()> f;
|
||||||
|
int ret;
|
||||||
|
{
|
||||||
|
S s;
|
||||||
|
f = [&s] { return s.f; };
|
||||||
|
}
|
||||||
|
return f; // if the caller invokes the lambda, it will try to read the invalid
|
||||||
|
// stack address
|
||||||
|
}
|
||||||
|
|
||||||
|
int ref_capture_return_local_lambda_ok() {
|
||||||
|
S x;
|
||||||
|
auto f = [&x](void) -> S& {
|
||||||
|
// do not report this because there is a good chance that this function will
|
||||||
|
// only be used in the local scope
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
return f().f;
|
||||||
|
}
|
||||||
|
|
||||||
|
S& FN_ref_capture_return_local_lambda_bad() {
|
||||||
|
S x;
|
||||||
|
auto f = [&x](void) -> S& {
|
||||||
|
// no way to know if ok here
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
// woops, this returns a ref to a local!
|
||||||
|
return f();
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
struct B {
|
||||||
|
B(int v) : f(v){};
|
||||||
|
int f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WrapsB {
|
||||||
|
WrapsB(int f) { b = new B(f); }
|
||||||
|
B* b;
|
||||||
|
B* getb() { return b; };
|
||||||
|
~WrapsB() { delete b; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReferenceWrapperHeap {
|
||||||
|
ReferenceWrapperHeap(WrapsB& a) : b(a.getb()){};
|
||||||
|
B* b;
|
||||||
|
};
|
||||||
|
|
||||||
|
ReferenceWrapperHeap getwrapperHeap() {
|
||||||
|
WrapsB a(1);
|
||||||
|
return a; // We store a.b in ReferenceWrapper, but we delete a.b in the
|
||||||
|
// destructor of WrapsB
|
||||||
|
}
|
||||||
|
|
||||||
|
int FN_reference_wrapper_heap_bad() {
|
||||||
|
ReferenceWrapperHeap rw = getwrapperHeap();
|
||||||
|
return rw.b->f; // we want to report use after lifetime bug here
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ReferenceWrapperStack {
|
||||||
|
ReferenceWrapperStack(B& bref) : b(&bref){};
|
||||||
|
B* b;
|
||||||
|
};
|
||||||
|
|
||||||
|
ReferenceWrapperStack getwrapperStack() {
|
||||||
|
B b(1);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FN_reference_wrapper_stack_bad() {
|
||||||
|
ReferenceWrapperStack rw = getwrapperStack();
|
||||||
|
return rw.b->f; // we want to report use after lifetime bug here
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace returns {
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
int f_;
|
||||||
|
|
||||||
|
S(int f) : f_(f) {}
|
||||||
|
|
||||||
|
~S() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const int& FN_return_literal_stack_reference_bad() { return 1; }
|
||||||
|
|
||||||
|
const int& FN_return_variable_stack_reference1_bad() {
|
||||||
|
const int& x = 2;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int& FN_return_variable_stack_reference2_bad() {
|
||||||
|
const int& x = 2;
|
||||||
|
const int& y = x;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int return_read_of_stack_reference_ok() {
|
||||||
|
const int& x = 2;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int& return_formal_reference_ok(int& formal) { return formal; }
|
||||||
|
|
||||||
|
const int& return_reference_to_formal_pointer_ok(const int* formal) {
|
||||||
|
const int& local = *formal;
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern const int& callee();
|
||||||
|
|
||||||
|
const int& return_reference_from_callee_ok() {
|
||||||
|
const int& local = callee();
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int return_int_ok() { return 1; }
|
||||||
|
|
||||||
|
const bool return_comparison_temp_ok() { return 1 != 2; }
|
||||||
|
|
||||||
|
const bool compare_local_refs_ok() {
|
||||||
|
const int& local1 = 1;
|
||||||
|
const int& local2 = 1;
|
||||||
|
return local1 != local2;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int& global;
|
||||||
|
|
||||||
|
const int& return_global_reference_ok() { return global; }
|
||||||
|
|
||||||
|
struct MemberReference {
|
||||||
|
int& member1;
|
||||||
|
|
||||||
|
int& return_member_reference_ok() { return member1; }
|
||||||
|
|
||||||
|
int* member2;
|
||||||
|
int* return_member_reference_indirect_ok() {
|
||||||
|
int* local = member2;
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const char* const kOptions;
|
||||||
|
|
||||||
|
const char* return_field_addr_ternary_ok() {
|
||||||
|
const char* const* const t = &kOptions;
|
||||||
|
return t ? *t : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int* FN_return_stack_pointer_bad() {
|
||||||
|
int x = 3;
|
||||||
|
return &x;
|
||||||
|
}
|
||||||
|
|
||||||
|
S* return_static_local_ok() {
|
||||||
|
S* local;
|
||||||
|
static S s{1};
|
||||||
|
local = &s;
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
S* return_static_local_inner_scope_ok(bool b) {
|
||||||
|
S* local = nullptr;
|
||||||
|
if (b) {
|
||||||
|
static S s{1};
|
||||||
|
local = &s;
|
||||||
|
// destructor for s gets called here, but it shouldn't be
|
||||||
|
}
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
int* return_formal_pointer_ok(int* formal) { return formal; }
|
||||||
|
|
||||||
|
int* return_deleted_bad() {
|
||||||
|
int* x = new int;
|
||||||
|
*x = 2;
|
||||||
|
delete x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this *could* be ok depending on what the destructor does, but there's
|
||||||
|
// probably no good reason to do it
|
||||||
|
S* return_destructed_pointer_bad() {
|
||||||
|
S* s = new S(1);
|
||||||
|
s->~S();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* return_nullptr1_ok() { return nullptr; }
|
||||||
|
|
||||||
|
const char* return_nullptr2_ok() {
|
||||||
|
const char* local = nullptr;
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct A {
|
||||||
|
~A();
|
||||||
|
};
|
||||||
|
|
||||||
|
int try_catch_return_ok() {
|
||||||
|
A a;
|
||||||
|
try {
|
||||||
|
return 1;
|
||||||
|
} catch (...) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace returns
|
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct Simple {
|
||||||
|
int f;
|
||||||
|
};
|
||||||
|
|
||||||
|
void deref_deleted_bad() {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
delete s;
|
||||||
|
Simple tmp = *s;
|
||||||
|
}
|
||||||
|
|
||||||
|
Simple* return_deleted_bad() {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
delete s;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
Simple* reassign_deleted_ok() {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
delete s;
|
||||||
|
s = new Simple{2};
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reassign_field_of_deleted_bad() {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
delete s;
|
||||||
|
s->f = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reassign_field_of_reinitialized_ok(Simple* tmp) {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
delete s;
|
||||||
|
s = tmp;
|
||||||
|
s->f = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
void double_delete_bad() {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
delete s;
|
||||||
|
delete s;
|
||||||
|
}
|
||||||
|
|
||||||
|
Simple* delete_in_branch_bad(bool b) {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
if (b) {
|
||||||
|
delete s;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_in_branch_ok(bool b) {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
if (b) {
|
||||||
|
delete s;
|
||||||
|
} else {
|
||||||
|
delete s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void use_in_branch_bad(bool b) {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
delete s;
|
||||||
|
if (b) {
|
||||||
|
auto tmp = *s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_in_loop_bad() {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
delete s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_in_loop_ok() {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
delete s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FP_delete_ref_in_loop_ok(int j, std::vector<std::string> v) {
|
||||||
|
int i = 0;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
auto s = &v[i];
|
||||||
|
delete s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void use_in_loop_bad() {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
delete s;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
s->f = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Simple* FP_gated_delete_abort_ok(bool b) {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
if (b) {
|
||||||
|
delete s;
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
Simple* FP_gated_exit_abort_ok(bool b) {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
if (b) {
|
||||||
|
delete s;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
Simple* FP_gated_delete_throw_ok(bool b) {
|
||||||
|
auto s = new Simple{1};
|
||||||
|
if (b) {
|
||||||
|
delete s;
|
||||||
|
throw 5;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
int* f;
|
||||||
|
S(int i) {
|
||||||
|
f = new int;
|
||||||
|
*f = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing: operator= to copy the pointer. double delete can happen if
|
||||||
|
// operator= is called
|
||||||
|
|
||||||
|
~S() { delete f; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// destructor called at end of function, no issues
|
||||||
|
void normal_scope_destructor_ok() { S s(1); }
|
||||||
|
|
||||||
|
void nested_scope_destructor_ok() {
|
||||||
|
{ S s(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
int FP_reinit_after_explicit_destructor_ok() {
|
||||||
|
S s(1);
|
||||||
|
s.~S();
|
||||||
|
s = S(2);
|
||||||
|
return *s.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void placement_new_explicit_destructor_ok() {
|
||||||
|
char buf[sizeof(S)];
|
||||||
|
{
|
||||||
|
S* s = new (buf) S(1);
|
||||||
|
s->~S();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// this use of [buf] shouldn't be flagged
|
||||||
|
S* s = new (buf) S(2);
|
||||||
|
s->~S();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void double_destructor_bad() {
|
||||||
|
S s(1);
|
||||||
|
s.~S();
|
||||||
|
// destructor will be called again after S goes out of scope, which is
|
||||||
|
// undefined behavior
|
||||||
|
}
|
||||||
|
|
||||||
|
int use_after_destructor_bad() {
|
||||||
|
S s(1);
|
||||||
|
s.~S();
|
||||||
|
int ret = *s.f;
|
||||||
|
s = S{2};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't get this yet because we assume operator= copies resources correctly
|
||||||
|
// (but this isn't true for S)
|
||||||
|
void FN_use_after_scope1_bad() {
|
||||||
|
S s(1);
|
||||||
|
{
|
||||||
|
S tmp(2);
|
||||||
|
s = tmp; // translated as operator=(s, tmp)
|
||||||
|
} // destructor for tmp runs here
|
||||||
|
// destructor for s here; second time the value held by s has been destructed
|
||||||
|
}
|
||||||
|
|
||||||
|
void FN_use_after_scope2_bad() {
|
||||||
|
S s(1);
|
||||||
|
{
|
||||||
|
s = S(1);
|
||||||
|
} // destructor runs here, but our frontend currently doesn't insert it
|
||||||
|
}
|
||||||
|
|
||||||
|
struct POD {
|
||||||
|
int f;
|
||||||
|
};
|
||||||
|
|
||||||
|
// this code is ok since double-destructing POD struct is ok
|
||||||
|
void destruct_twice_ok() {
|
||||||
|
POD p{1};
|
||||||
|
{
|
||||||
|
POD tmp{2};
|
||||||
|
p = tmp;
|
||||||
|
} // destructor for tmp
|
||||||
|
} // destructor for p runs here, but it's harmless
|
||||||
|
|
||||||
|
class Subclass : virtual POD {
|
||||||
|
int* f;
|
||||||
|
Subclass() { f = new int; }
|
||||||
|
|
||||||
|
/** frontend code for this destructor will be:
|
||||||
|
* ~Subclass:
|
||||||
|
* __infer_inner_destructor_~Subclass(this)
|
||||||
|
* __infer_inner_destructor_~POD(this)
|
||||||
|
*
|
||||||
|
* __infer_inner_destructor_~Subclass:
|
||||||
|
* delete f;
|
||||||
|
*
|
||||||
|
* We need to be careful not to warn that this has been double-destructed
|
||||||
|
*/
|
||||||
|
~Subclass() { delete f; }
|
||||||
|
};
|
||||||
|
|
||||||
|
void FP_basic_placement_new_ok() {
|
||||||
|
S* ptr = new S(1);
|
||||||
|
S* tptr = new (ptr) S(1);
|
||||||
|
tptr->~S();
|
||||||
|
delete[] ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
S* destruct_pointer_contents_then_placement_new1_ok(S* s) {
|
||||||
|
s->~S();
|
||||||
|
new (s) S(1);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
S* placement_new_aliasing1_bad() {
|
||||||
|
S* s = new S(1);
|
||||||
|
s->~S();
|
||||||
|
auto alias = new (s) S(2);
|
||||||
|
delete alias; // this deletes s too
|
||||||
|
return s; // bad, returning freed memory
|
||||||
|
}
|
||||||
|
|
||||||
|
S* placement_new_aliasing2_bad() {
|
||||||
|
S* s = new S(1);
|
||||||
|
s->~S();
|
||||||
|
auto alias = new (s) S(2);
|
||||||
|
delete s; // this deletes alias too
|
||||||
|
return alias; // bad, returning freed memory
|
||||||
|
}
|
||||||
|
|
||||||
|
void placement_new_non_var_ok() {
|
||||||
|
struct M {
|
||||||
|
S* s;
|
||||||
|
} m;
|
||||||
|
m.s = new S(1);
|
||||||
|
m.s->~S();
|
||||||
|
new (m.s) S(2);
|
||||||
|
delete m.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void return_placement_new_ok() {
|
||||||
|
auto mem = new S(1);
|
||||||
|
return new (mem) S(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FP_destructor_in_loop_ok() {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
S s(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FN_use_after_scope3_bad() {
|
||||||
|
int* p;
|
||||||
|
{
|
||||||
|
int value = 3;
|
||||||
|
p = &value;
|
||||||
|
} // we do not know in the plugin that value is out of scope
|
||||||
|
return *p;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct C {
|
||||||
|
C(int v) : f(v){};
|
||||||
|
~C();
|
||||||
|
int f;
|
||||||
|
};
|
||||||
|
|
||||||
|
int use_after_scope4_bad() {
|
||||||
|
C* pc;
|
||||||
|
{
|
||||||
|
C c(3);
|
||||||
|
pc = &c;
|
||||||
|
}
|
||||||
|
return pc->f;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
~B();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct A {
|
||||||
|
~A() { (void)*f; }
|
||||||
|
const B* f;
|
||||||
|
};
|
||||||
|
|
||||||
|
void FN_destructor_order_bad() {
|
||||||
|
A a;
|
||||||
|
B b;
|
||||||
|
a.f = &b;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct A2 {
|
||||||
|
~A2() {}
|
||||||
|
const B* f;
|
||||||
|
};
|
||||||
|
|
||||||
|
// need interprocedural analysis to fix this
|
||||||
|
void FP_destructor_order_empty_destructor_ok() {
|
||||||
|
A2 a;
|
||||||
|
B b;
|
||||||
|
a.f = &b;
|
||||||
|
}
|
Loading…
Reference in new issue