Index: clang/docs/analyzer/checkers.rst =================================================================== --- clang/docs/analyzer/checkers.rst +++ clang/docs/analyzer/checkers.rst @@ -1834,6 +1834,30 @@ 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 `_. + +.. 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 @@ .. 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 } Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -758,6 +758,11 @@ Documentation, Hidden; +def CXXArrayDeleteChecker : Checker<"ArrayDelete">, + HelpText<"Reports destructions of arrays of polymorphic objects that are " + "destructed as their base class.">, + Documentation; + def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">, HelpText<"Reports destructions of polymorphic objects with a non-virtual " "destructor in their base class">, Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -30,12 +30,12 @@ CloneChecker.cpp ContainerModeling.cpp ConversionChecker.cpp + CXXDeleteChecker.cpp CXXSelfAssignmentChecker.cpp DeadStoresChecker.cpp DebugCheckers.cpp DebugContainerModeling.cpp DebugIteratorModeling.cpp - DeleteWithNonVirtualDtorChecker.cpp DereferenceChecker.cpp DirectIvarAssignment.cpp DivZeroChecker.cpp Index: clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp @@ -0,0 +1,241 @@ +//=== 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. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the following new checkers for C++ delete expressions: +// +// * DeleteWithNonVirtualDtorChecker +// Defines a checker for the OOP52-CPP CERT rule: Do not delete a +// polymorphic object without a virtual destructor. +// +// 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. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { +class CXXDeleteChecker : public Checker> { +protected: + class PtrCastVisitor : public BugReporterVisitor { + public: + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + } + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; + }; + + 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; +}; + +class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker { + mutable std::unique_ptr BT; + + void + checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const override; +}; + +class CXXArrayDeleteChecker : public CXXDeleteChecker { + mutable std::unique_ptr 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(); + const auto *DerivedClassRegion = MR->getBaseRegion()->getAs(); + 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->getDestructor()->isVirtual()) + return; + + if (!DerivedClass->isDerivedFrom(BaseClass)) + return; + + if (!BT) + BT.reset(new BugType(this, + "Destruction of a polymorphic object with no " + "virtual destructor", + "Logic error")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + auto R = + std::make_unique(*BT, BT->getDescription(), N); + + // Mark region of problematic base class for later use in the BugVisitor. + R->markInteresting(BaseClassRegion); + R->addVisitor(); + C.emitReport(std::move(R)); +} + +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(*BT, OS.str(), N); + + // Mark region of problematic base class for later use in the BugVisitor. + R->markInteresting(BaseClassRegion); + R->addVisitor(); + 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; + + const auto *CastE = dyn_cast(S); + if (!CastE) + 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(); + if (!M) + return nullptr; + + // Check if target region was marked as problematic previously. + if (!BR.isInteresting(M)) + return nullptr; + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + + OS << "Casting from '" << SourceType.getAsString() << "' to '" + << TargetType.getAsString() << "' here"; + + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared(Pos, OS.str(), /*addPosRange=*/true); +} + +void ento::registerCXXArrayDeleteChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterCXXArrayDeleteChecker(const CheckerManager &mgr) { + return true; +} + +void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( + const CheckerManager &mgr) { + return true; +} Index: clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp +++ /dev/null @@ -1,155 +0,0 @@ -//===-- DeleteWithNonVirtualDtorChecker.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. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines a checker for the OOP52-CPP CERT rule: Do not delete a polymorphic -// object without a virtual destructor. -// -// 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. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" - -using namespace clang; -using namespace ento; - -namespace { -class DeleteWithNonVirtualDtorChecker - : public Checker> { - mutable std::unique_ptr BT; - - class DeleteBugVisitor : public BugReporterVisitor { - public: - DeleteBugVisitor() = default; - void Profile(llvm::FoldingSetNodeID &ID) const override { - static int X = 0; - ID.AddPointer(&X); - } - PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - PathSensitiveBugReport &BR) override; - - private: - bool Satisfied = false; - }; - -public: - void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; -}; -} // end anonymous namespace - -void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE, - CheckerContext &C) const { - const Expr *DeletedObj = DE->getArgument(); - const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); - if (!MR) - return; - - const auto *BaseClassRegion = MR->getAs(); - const auto *DerivedClassRegion = MR->getBaseRegion()->getAs(); - if (!BaseClassRegion || !DerivedClassRegion) - return; - - 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; - - if (!DerivedClass->isDerivedFrom(BaseClass)) - return; - - if (!BT) - BT.reset(new BugType(this, - "Destruction of a polymorphic object with no " - "virtual destructor", - "Logic error")); - - ExplodedNode *N = C.generateNonFatalErrorNode(); - if (!N) - return; - auto R = std::make_unique(*BT, BT->getDescription(), N); - - // Mark region of problematic base class for later use in the BugVisitor. - R->markInteresting(BaseClassRegion); - R->addVisitor(std::make_unique()); - 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; - - const Stmt *S = N->getStmtForDiagnostics(); - if (!S) - return nullptr; - - const auto *CastE = dyn_cast(S); - if (!CastE) - return nullptr; - - // Only interested in DerivedToBase implicit casts. - // Explicit casts can have different CastKinds. - if (const auto *ImplCastE = dyn_cast(CastE)) { - if (ImplCastE->getCastKind() != CK_DerivedToBase) - return nullptr; - } - - // Region associated with the current cast expression. - const MemRegion *M = N->getSVal(CastE).getAsRegion(); - if (!M) - return nullptr; - - // Check if target region was marked as problematic previously. - 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"; - PathDiagnosticLocation Pos(S, BRC.getSourceManager(), - N->getLocationContext()); - return std::make_shared(Pos, OS.str(), true); -} - -void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { - mgr.registerChecker(); -} - -bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( - const CheckerManager &mgr) { - return true; -} Index: clang/test/Analysis/ArrayDelete.cpp =================================================================== --- /dev/null +++ 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(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(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(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(b4); + DoubleDerived *dd4 = static_cast(d4); + delete[] dd4; // no-warning + + Base *b5 = new DoubleDerived[10]; // expected-note{{Casting from 'DoubleDerived' to 'Base' here}} + DoubleDerived *dd5 = static_cast(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-warning + + // FIXME: Displaying casts of reference types is not supported. + Derived &d2 = static_cast(b2); // no-warning + + Derived *d = &d2; // no-warning + 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}} +} Index: clang/test/Analysis/DeleteWithNonVirtualDtor.cpp =================================================================== --- clang/test/Analysis/DeleteWithNonVirtualDtor.cpp +++ clang/test/Analysis/DeleteWithNonVirtualDtor.cpp @@ -33,7 +33,7 @@ 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 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 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(sc); // expected-note{{Conversion from derived to base happened here}} + NonVirtual *sc2 = reinterpret_cast(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(dd); - dd = reinterpret_cast(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(dd); // expected-note {{Casting from 'NonVirtual' to 'NVDerived' here}} + dd = reinterpret_cast(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}} } Index: clang/www/analyzer/alpha_checks.html =================================================================== --- clang/www/analyzer/alpha_checks.html +++ clang/www/analyzer/alpha_checks.html @@ -330,6 +330,26 @@ + + + +