111 changes: 111 additions & 0 deletions clang/test/Analysis/ArrayDelete.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.cplusplus.ArrayDelete -std=c++11 -verify -analyzer-output=text %s

struct Base {
virtual ~Base() = default;
};

struct Derived : public Base {};

struct DoubleDerived : public Derived {};

Derived *get();

Base *create() {
Base *b = new Derived[3]; // expected-note{{Casting from 'Derived' to 'Base' here}}
return b;
}

void sink(Base *b) {
delete[] b; // expected-warning{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}
// expected-note@-1{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}
}

void sink_cast(Base *b) {
delete[] static_cast<Derived*>(b); // no-warning
}

void sink_derived(Derived *d) {
delete[] d; // no-warning
}

void same_function() {
Base *sd = new Derived[10]; // expected-note{{Casting from 'Derived' to 'Base' here}}
delete[] sd; // expected-warning{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}
// expected-note@-1{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}

Base *dd = new DoubleDerived[10]; // expected-note{{Casting from 'DoubleDerived' to 'Base' here}}
delete[] dd; // expected-warning{{Deleting an array of 'DoubleDerived' objects as their base class 'Base' is undefined}}
// expected-note@-1{{Deleting an array of 'DoubleDerived' objects as their base class 'Base' is undefined}}
}

void different_function() {
Base *assigned = get(); // expected-note{{Casting from 'Derived' to 'Base' here}}
delete[] assigned; // expected-warning{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}
// expected-note@-1{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}

Base *indirect;
indirect = get(); // expected-note{{Casting from 'Derived' to 'Base' here}}
delete[] indirect; // expected-warning{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}
// expected-note@-1{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}

Base *created = create(); // expected-note{{Calling 'create'}}
// expected-note@-1{{Returning from 'create'}}
delete[] created; // expected-warning{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}
// expected-note@-1{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}

Base *sb = new Derived[10]; // expected-note{{Casting from 'Derived' to 'Base' here}}
sink(sb); // expected-note{{Calling 'sink'}}
}

void safe_function() {
Derived *d = new Derived[10];
delete[] d; // no-warning

Base *b = new Derived[10];
delete[] static_cast<Derived*>(b); // no-warning

Base *sb = new Derived[10];
sink_cast(sb); // no-warning

Derived *sd = new Derived[10];
sink_derived(sd); // no-warning
}

void multiple_derived() {
Base *b = new DoubleDerived[10]; // expected-note{{Casting from 'DoubleDerived' to 'Base' here}}
delete[] b; // expected-warning{{Deleting an array of 'DoubleDerived' objects as their base class 'Base' is undefined}}
// expected-note@-1{{Deleting an array of 'DoubleDerived' objects as their base class 'Base' is undefined}}

Base *b2 = new DoubleDerived[10]; // expected-note{{Casting from 'DoubleDerived' to 'Base' here}}
Derived *d2 = static_cast<Derived*>(b2); // expected-note{{Casting from 'Base' to 'Derived' here}}
delete[] d2; // expected-warning{{Deleting an array of 'DoubleDerived' objects as their base class 'Derived' is undefined}}
// expected-note@-1{{Deleting an array of 'DoubleDerived' objects as their base class 'Derived' is undefined}}

Derived *d3 = new DoubleDerived[10]; // expected-note{{Casting from 'DoubleDerived' to 'Derived' here}}
Base *b3 = d3; // expected-note{{Casting from 'Derived' to 'Base' here}}
delete[] b3; // expected-warning{{Deleting an array of 'DoubleDerived' objects as their base class 'Base' is undefined}}
// expected-note@-1{{Deleting an array of 'DoubleDerived' objects as their base class 'Base' is undefined}}

Base *b4 = new DoubleDerived[10];
Derived *d4 = static_cast<Derived*>(b4);
DoubleDerived *dd4 = static_cast<DoubleDerived*>(d4);
delete[] dd4; // no-warning

Base *b5 = new DoubleDerived[10]; // expected-note{{Casting from 'DoubleDerived' to 'Base' here}}
DoubleDerived *dd5 = static_cast<DoubleDerived*>(b5); // expected-note{{Casting from 'Base' to 'DoubleDerived' here}}
Derived *d5 = dd5; // expected-note{{Casting from 'DoubleDerived' to 'Derived' here}}
delete[] d5; // expected-warning{{Deleting an array of 'DoubleDerived' objects as their base class 'Derived' is undefined}}
// expected-note@-1{{Deleting an array of 'DoubleDerived' objects as their base class 'Derived' is undefined}}
}

void unrelated_casts() {
Base *b = new DoubleDerived[10]; // expected-note{{Casting from 'DoubleDerived' to 'Base' here}}
Base &b2 = *b; // no-note: See the FIXME.

// FIXME: Displaying casts of reference types is not supported.
Derived &d2 = static_cast<Derived&>(b2); // no-note: See the FIXME.

Derived *d = &d2; // no-note: See the FIXME.
delete[] d; // expected-warning{{Deleting an array of 'DoubleDerived' objects as their base class 'Derived' is undefined}}
// expected-note@-1{{Deleting an array of 'DoubleDerived' objects as their base class 'Derived' is undefined}}
}
34 changes: 17 additions & 17 deletions clang/test/Analysis/DeleteWithNonVirtualDtor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct ImplicitNVDerived : public ImplicitNV {};
NVDerived *get();

NonVirtual *create() {
NonVirtual *x = new NVDerived(); // expected-note{{Conversion from derived to base happened here}}
NonVirtual *x = new NVDerived(); // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
return x;
}

Expand All @@ -52,32 +52,32 @@ void sinkParamCast(NVDerived *z) {

void singleDerived() {
NonVirtual *sd;
sd = new NVDerived(); // expected-note{{Conversion from derived to base happened here}}
sd = new NVDerived(); // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
delete sd; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}

void singleDerivedArr() {
NonVirtual *sda = new NVDerived[5]; // expected-note{{Conversion from derived to base happened here}}
NonVirtual *sda = new NVDerived[5]; // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
delete[] sda; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}

void doubleDerived() {
NonVirtual *dd = new NVDoubleDerived(); // expected-note{{Conversion from derived to base happened here}}
NonVirtual *dd = new NVDoubleDerived(); // expected-note{{Casting from 'NVDoubleDerived' to 'NonVirtual' here}}
delete (dd); // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}

void assignThroughFunction() {
NonVirtual *atf = get(); // expected-note{{Conversion from derived to base happened here}}
NonVirtual *atf = get(); // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
delete atf; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}

void assignThroughFunction2() {
NonVirtual *atf2;
atf2 = get(); // expected-note{{Conversion from derived to base happened here}}
atf2 = get(); // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
delete atf2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}
Expand All @@ -90,49 +90,49 @@ void createThroughFunction() {
}

void deleteThroughFunction() {
NonVirtual *dtf = new NVDerived(); // expected-note{{Conversion from derived to base happened here}}
NonVirtual *dtf = new NVDerived(); // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
sink(dtf); // expected-note{{Calling 'sink'}}
}

void singleCastCStyle() {
NVDerived *sccs = new NVDerived();
NonVirtual *sccs2 = (NonVirtual*)sccs; // expected-note{{Conversion from derived to base happened here}}
NonVirtual *sccs2 = (NonVirtual*)sccs; // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
delete sccs2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}

void doubleCastCStyle() {
NonVirtual *dccs = new NVDerived();
NVDerived *dccs2 = (NVDerived*)dccs;
dccs = (NonVirtual*)dccs2; // expected-note{{Conversion from derived to base happened here}}
NonVirtual *dccs = new NVDerived(); // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
NVDerived *dccs2 = (NVDerived*)dccs; // expected-note{{Casting from 'NonVirtual' to 'NVDerived' here}}
dccs = (NonVirtual*)dccs2; // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
delete dccs; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}

void singleCast() {
NVDerived *sc = new NVDerived();
NonVirtual *sc2 = reinterpret_cast<NonVirtual*>(sc); // expected-note{{Conversion from derived to base happened here}}
NonVirtual *sc2 = reinterpret_cast<NonVirtual*>(sc); // expected-note{{Casting from 'NVDerived' to 'NonVirtual' here}}
delete sc2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}

void doubleCast() {
NonVirtual *dd = new NVDerived();
NVDerived *dd2 = reinterpret_cast<NVDerived*>(dd);
dd = reinterpret_cast<NonVirtual*>(dd2); // expected-note {{Conversion from derived to base happened here}}
NonVirtual *dd = new NVDerived(); // expected-note {{Casting from 'NVDerived' to 'NonVirtual' here}}
NVDerived *dd2 = reinterpret_cast<NVDerived*>(dd); // expected-note {{Casting from 'NonVirtual' to 'NVDerived' here}}
dd = reinterpret_cast<NonVirtual*>(dd2); // expected-note {{Casting from 'NVDerived' to 'NonVirtual' here}}
delete dd; // expected-warning {{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}

void implicitNV() {
ImplicitNV *invd = new ImplicitNVDerived(); // expected-note{{Conversion from derived to base happened here}}
ImplicitNV *invd = new ImplicitNVDerived(); // expected-note{{Casting from 'ImplicitNVDerived' to 'ImplicitNV' here}}
delete invd; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}

void doubleDecl() {
ImplicitNV *dd1, *dd2;
dd1 = new ImplicitNVDerived(); // expected-note{{Conversion from derived to base happened here}}
dd1 = new ImplicitNVDerived(); // expected-note{{Casting from 'ImplicitNVDerived' to 'ImplicitNV' here}}
delete dd1; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}}
// expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}}
}
Expand Down
24 changes: 22 additions & 2 deletions clang/www/analyzer/alpha_checks.html
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,26 @@ <h3 id="cplusplus_alpha_checkers">C++ Alpha Checkers</h3>
<tbody>


<tr><td><a id="alpha.cplusplus.ArrayDelete"><div class="namedescr expandable"><span class="name">
alpha.cplusplus.ArrayDelete</span><span class="lang">
(C++)</span><div class="descr">
Reports destructions of arrays of polymorphic objects that are destructed as
their base class
</div></div></a></td>
<td><div class="exampleContainer expandable">
<div class="example"><pre>
Base *create() {
Base *x = new Derived[10]; // note: Casting from 'Derived' to 'Base' here
return x;
}

void sink(Base *x) {
delete[] x; // warn: Deleting an array of 'Derived' objects as their base class 'Base' undefined
}

</pre></div></div></td></tr>


<tr><td><a id="alpha.cplusplus.DeleteWithNonVirtualDtor"><div class="namedescr expandable"><span class="name">
alpha.cplusplus.DeleteWithNonVirtualDtor</span><span class="lang">
(C++)</span><div class="descr">
Expand All @@ -339,8 +359,8 @@ <h3 id="cplusplus_alpha_checkers">C++ Alpha Checkers</h3>
<td><div class="exampleContainer expandable">
<div class="example"><pre>
NonVirtual *create() {
NonVirtual *x = new NVDerived(); // note: conversion from derived to base
// happened here
NonVirtual *x = new NVDerived(); // note: Casting from 'NVDerived' to
// 'NonVirtual' here
return x;
}

Expand Down