[clang] 0e246bb - [clang][analyzer] Add C++ array delete checker

Viktor Cseh via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 10 01:37:46 PDT 2023


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 at -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 at -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 at -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 at -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 at -1{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}
+
+    Base *created = create(); // expected-note{{Calling 'create'}}
+    // expected-note at -1{{Returning from 'create'}}
+    delete[] created; // expected-warning{{Deleting an array of 'Derived' objects as their base class 'Base' is undefined}}
+    // expected-note at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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;
 }
 


        


More information about the cfe-commits mailing list