Author: Viktor Cseh
Date: 2023-10-10T09:37:02+01:00
New Revision: 0e246bb67573799409d0085b89902a330998ddcc

URL: 
https://github.com/llvm/llvm-project/commit/0e246bb67573799409d0085b89902a330998ddcc
DIFF: 
https://github.com/llvm/llvm-project/commit/0e246bb67573799409d0085b89902a330998ddcc.diff

LOG: [clang][analyzer] Add C++ array delete checker

This checker reports cases where an array of polymorphic objects are
deleted as their base class. Deleting an array where the array's static
type is different from its dynamic type is undefined.

Since the checker is similar to DeleteWithNonVirtualDtorChecker, I
refactored that checker to support more detection types.

This checker corresponds to the SEI Cert rule EXP51-CPP: Do not delete
an array through a pointer of the incorrect type.

Differential Revision: https://reviews.llvm.org/D158156

Added: 
    clang/test/Analysis/ArrayDelete.cpp

Modified: 
    clang/docs/analyzer/checkers.rst
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp
    clang/test/Analysis/DeleteWithNonVirtualDtor.cpp
    clang/www/analyzer/alpha_checks.html

Removed: 
    


################################################################################
diff  --git a/clang/docs/analyzer/checkers.rst 
b/clang/docs/analyzer/checkers.rst
index dbd6d7787823530..81f333e644f31c9 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -1834,6 +1834,30 @@ Either the comparison is useless or there is division by 
zero.
 alpha.cplusplus
 ^^^^^^^^^^^^^^^
 
+.. _alpha-cplusplus-ArrayDelete:
+
+alpha.cplusplus.ArrayDelete (C++)
+"""""""""""""""""""""""""""""""""
+Reports destructions of arrays of polymorphic objects that are destructed as 
their base class.
+This checker corresponds to the CERT rule `EXP51-CPP: Do not delete an array 
through a pointer of the incorrect type 
<https://wiki.sei.cmu.edu/confluence/display/cplusplus/EXP51-CPP.+Do+not+delete+an+array+through+a+pointer+of+the+incorrect+type>`_.
+
+.. code-block:: cpp
+
+ class Base {
+   virtual ~Base() {}
+ };
+ class Derived : public Base {}
+
+ Base *create() {
+   Base *x = new Derived[10]; // note: Casting from 'Derived' to 'Base' here
+   return x;
+ }
+
+ void foo() {
+   Base *x = create();
+   delete[] x; // warn: Deleting an array of 'Derived' objects as their base 
class 'Base' is undefined
+ }
+
 .. _alpha-cplusplus-DeleteWithNonVirtualDtor:
 
 alpha.cplusplus.DeleteWithNonVirtualDtor (C++)
@@ -1842,13 +1866,17 @@ Reports destructions of polymorphic objects with a 
non-virtual destructor in the
 
 .. code-block:: cpp
 
+ class NonVirtual {};
+ class NVDerived : public NonVirtual {};
+
  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;
  }
 
- void sink(NonVirtual *x) {
+ void foo() {
+   NonVirtual *x = create();
    delete x; // warn: destruction of a polymorphic object with no virtual
              //       destructor
  }

diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 65c1595eb6245dd..4ca8c98af8706aa 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -758,6 +758,11 @@ def ContainerModeling : Checker<"ContainerModeling">,
   Documentation<NotDocumented>,
   Hidden;
 
+def CXXArrayDeleteChecker : Checker<"ArrayDelete">,
+  HelpText<"Reports destructions of arrays of polymorphic objects that are "
+           "destructed as their base class.">,
+  Documentation<HasDocumentation>;
+
 def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">,
   HelpText<"Reports destructions of polymorphic objects with a non-virtual "
            "destructor in their base class">,

diff  --git a/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp
index 3c142b49ff7288d..1a1f5c530294038 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp
@@ -1,4 +1,4 @@
-//===-- DeleteWithNonVirtualDtorChecker.cpp -----------------------*- C++ 
-*--//
+//=== CXXDeleteChecker.cpp -------------------------------------*- C++ 
-*--===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,17 +6,25 @@
 //
 
//===----------------------------------------------------------------------===//
 //
-// Defines a checker for the OOP52-CPP CERT rule: Do not delete a polymorphic
-// object without a virtual destructor.
+// This file defines the following new checkers for C++ delete expressions:
 //
-// Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor report if
-// an object with a virtual function but a non-virtual destructor exists or is
-// deleted, respectively.
+//   * DeleteWithNonVirtualDtorChecker
+//       Defines a checker for the OOP52-CPP CERT rule: Do not delete a
+//       polymorphic object without a virtual destructor.
 //
-// This check exceeds them by comparing the dynamic and static types of the
-// object at the point of destruction and only warns if it happens through a
-// pointer to a base type without a virtual destructor. The check places a note
-// at the last point where the conversion from derived to base happened.
+//       Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor
+//       report if an object with a virtual function but a non-virtual
+//       destructor exists or is deleted, respectively.
+//
+//       This check exceeds them by comparing the dynamic and static types of
+//       the object at the point of destruction and only warns if it happens
+//       through a pointer to a base type without a virtual destructor. The
+//       check places a note at the last point where the conversion from
+//       derived to base happened.
+//
+//   * CXXArrayDeleteChecker
+//       Defines a checker for the EXP51-CPP CERT rule: Do not delete an array
+//       through a pointer of the incorrect type.
 //
 
//===----------------------------------------------------------------------===//
 
@@ -34,13 +42,10 @@ using namespace clang;
 using namespace ento;
 
 namespace {
-class DeleteWithNonVirtualDtorChecker
-    : public Checker<check::PreStmt<CXXDeleteExpr>> {
-  mutable std::unique_ptr<BugType> BT;
-
-  class DeleteBugVisitor : public BugReporterVisitor {
+class CXXDeleteChecker : public Checker<check::PreStmt<CXXDeleteExpr>> {
+protected:
+  class PtrCastVisitor : public BugReporterVisitor {
   public:
-    DeleteBugVisitor() = default;
     void Profile(llvm::FoldingSetNodeID &ID) const override {
       static int X = 0;
       ID.AddPointer(&X);
@@ -48,37 +53,67 @@ class DeleteWithNonVirtualDtorChecker
     PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
                                      BugReporterContext &BRC,
                                      PathSensitiveBugReport &BR) override;
-
-  private:
-    bool Satisfied = false;
   };
 
+  virtual void
+  checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
+                       const TypedValueRegion *BaseClassRegion,
+                       const SymbolicRegion *DerivedClassRegion) const = 0;
+
 public:
   void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
 };
-} // end anonymous namespace
 
-void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE,
-                                                   CheckerContext &C) const {
+class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker {
+  mutable std::unique_ptr<BugType> BT;
+
+  void
+  checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
+                       const TypedValueRegion *BaseClassRegion,
+                       const SymbolicRegion *DerivedClassRegion) const 
override;
+};
+
+class CXXArrayDeleteChecker : public CXXDeleteChecker {
+  mutable std::unique_ptr<BugType> BT;
+
+  void
+  checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
+                       const TypedValueRegion *BaseClassRegion,
+                       const SymbolicRegion *DerivedClassRegion) const 
override;
+};
+} // namespace
+
+void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr *DE,
+                                    CheckerContext &C) const {
   const Expr *DeletedObj = DE->getArgument();
   const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion();
   if (!MR)
     return;
 
+  OverloadedOperatorKind DeleteKind =
+      DE->getOperatorDelete()->getOverloadedOperator();
+
+  if (DeleteKind != OO_Delete && DeleteKind != OO_Array_Delete)
+    return;
+
   const auto *BaseClassRegion = MR->getAs<TypedValueRegion>();
   const auto *DerivedClassRegion = 
MR->getBaseRegion()->getAs<SymbolicRegion>();
   if (!BaseClassRegion || !DerivedClassRegion)
     return;
 
+  checkTypedDeleteExpr(DE, C, BaseClassRegion, DerivedClassRegion);
+}
+
+void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr(
+    const CXXDeleteExpr *DE, CheckerContext &C,
+    const TypedValueRegion *BaseClassRegion,
+    const SymbolicRegion *DerivedClassRegion) const {
   const auto *BaseClass = 
BaseClassRegion->getValueType()->getAsCXXRecordDecl();
   const auto *DerivedClass =
       DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
   if (!BaseClass || !DerivedClass)
     return;
 
-  if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
-    return;
-
   if (BaseClass->getDestructor()->isVirtual())
     return;
 
@@ -94,22 +129,65 @@ void DeleteWithNonVirtualDtorChecker::checkPreStmt(const 
CXXDeleteExpr *DE,
   ExplodedNode *N = C.generateNonFatalErrorNode();
   if (!N)
     return;
-  auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), 
N);
+  auto R =
+      std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
 
   // Mark region of problematic base class for later use in the BugVisitor.
   R->markInteresting(BaseClassRegion);
-  R->addVisitor(std::make_unique<DeleteBugVisitor>());
+  R->addVisitor<PtrCastVisitor>();
   C.emitReport(std::move(R));
 }
 
-PathDiagnosticPieceRef
-DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
-    const ExplodedNode *N, BugReporterContext &BRC,
-    PathSensitiveBugReport &BR) {
-  // Stop traversal after the first conversion was found on a path.
-  if (Satisfied)
-    return nullptr;
+void CXXArrayDeleteChecker::checkTypedDeleteExpr(
+    const CXXDeleteExpr *DE, CheckerContext &C,
+    const TypedValueRegion *BaseClassRegion,
+    const SymbolicRegion *DerivedClassRegion) const {
+  const auto *BaseClass = 
BaseClassRegion->getValueType()->getAsCXXRecordDecl();
+  const auto *DerivedClass =
+      DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
+  if (!BaseClass || !DerivedClass)
+    return;
 
+  if (DE->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete)
+    return;
+
+  if (!DerivedClass->isDerivedFrom(BaseClass))
+    return;
+
+  if (!BT)
+    BT.reset(new BugType(this,
+                         "Deleting an array of polymorphic objects "
+                         "is undefined",
+                         "Logic error"));
+
+  ExplodedNode *N = C.generateNonFatalErrorNode();
+  if (!N)
+    return;
+
+  SmallString<256> Buf;
+  llvm::raw_svector_ostream OS(Buf);
+
+  QualType SourceType = BaseClassRegion->getValueType();
+  QualType TargetType =
+      DerivedClassRegion->getSymbol()->getType()->getPointeeType();
+
+  OS << "Deleting an array of '" << TargetType.getAsString()
+     << "' objects as their base class '"
+     << SourceType.getAsString(C.getASTContext().getPrintingPolicy())
+     << "' is undefined";
+
+  auto R = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
+
+  // Mark region of problematic base class for later use in the BugVisitor.
+  R->markInteresting(BaseClassRegion);
+  R->addVisitor<PtrCastVisitor>();
+  C.emitReport(std::move(R));
+}
+
+PathDiagnosticPieceRef
+CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode *N,
+                                            BugReporterContext &BRC,
+                                            PathSensitiveBugReport &BR) {
   const Stmt *S = N->getStmtForDiagnostics();
   if (!S)
     return nullptr;
@@ -118,12 +196,12 @@ 
DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
   if (!CastE)
     return nullptr;
 
-  // Only interested in DerivedToBase implicit casts.
-  // Explicit casts can have 
diff erent CastKinds.
-  if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) {
-    if (ImplCastE->getCastKind() != CK_DerivedToBase)
-      return nullptr;
-  }
+  // FIXME: This way of getting base types does not support reference types.
+  QualType SourceType = CastE->getSubExpr()->getType()->getPointeeType();
+  QualType TargetType = CastE->getType()->getPointeeType();
+
+  if (SourceType.isNull() || TargetType.isNull() || SourceType == TargetType)
+    return nullptr;
 
   // Region associated with the current cast expression.
   const MemRegion *M = N->getSVal(CastE).getAsRegion();
@@ -134,15 +212,24 @@ 
DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
   if (!BR.isInteresting(M))
     return nullptr;
 
-  // Stop traversal on this path.
-  Satisfied = true;
-
   SmallString<256> Buf;
   llvm::raw_svector_ostream OS(Buf);
-  OS << "Conversion from derived to base happened here";
+
+  OS << "Casting from '" << SourceType.getAsString() << "' to '"
+     << TargetType.getAsString() << "' here";
+
   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
                              N->getLocationContext());
-  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
+  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
+                                                    /*addPosRange=*/true);
+}
+
+void ento::registerCXXArrayDeleteChecker(CheckerManager &mgr) {
+  mgr.registerChecker<CXXArrayDeleteChecker>();
+}
+
+bool ento::shouldRegisterCXXArrayDeleteChecker(const CheckerManager &mgr) {
+  return true;
 }
 
 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
@@ -150,6 +237,6 @@ void 
ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
 }
 
 bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
-                                                    const CheckerManager &mgr) 
{
+    const CheckerManager &mgr) {
   return true;
 }

diff  --git a/clang/test/Analysis/ArrayDelete.cpp 
b/clang/test/Analysis/ArrayDelete.cpp
new file mode 100644
index 000000000000000..3b8d49552376ed9
--- /dev/null
+++ b/clang/test/Analysis/ArrayDelete.cpp
@@ -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 
diff erent_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}}
+}

diff  --git a/clang/test/Analysis/DeleteWithNonVirtualDtor.cpp 
b/clang/test/Analysis/DeleteWithNonVirtualDtor.cpp
index a9b8a11f3482f3e..2809f3758eee1be 100644
--- a/clang/test/Analysis/DeleteWithNonVirtualDtor.cpp
+++ b/clang/test/Analysis/DeleteWithNonVirtualDtor.cpp
@@ -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;
 }
 
@@ -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}}
 }
@@ -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}}
 }

diff  --git a/clang/www/analyzer/alpha_checks.html 
b/clang/www/analyzer/alpha_checks.html
index 181ce1b9de591b3..cff0284777bc78a 100644
--- a/clang/www/analyzer/alpha_checks.html
+++ b/clang/www/analyzer/alpha_checks.html
@@ -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">
@@ -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;
 }
 


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to