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