15ffd83dbSDimitry Andric //==- CheckPlacementNew.cpp - Check for placement new operation --*- C++ -*-==//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric //
95ffd83dbSDimitry Andric // This file defines a check for misuse of the default placement new operator.
105ffd83dbSDimitry Andric //
115ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
125ffd83dbSDimitry Andric
13480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
16*fe6060f1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
17480093f4SDimitry Andric #include "llvm/Support/FormatVariadic.h"
18480093f4SDimitry Andric
19480093f4SDimitry Andric using namespace clang;
20480093f4SDimitry Andric using namespace ento;
21480093f4SDimitry Andric
22480093f4SDimitry Andric namespace {
23480093f4SDimitry Andric class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
24480093f4SDimitry Andric public:
25480093f4SDimitry Andric void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
26480093f4SDimitry Andric
27480093f4SDimitry Andric private:
285ffd83dbSDimitry Andric bool checkPlaceCapacityIsSufficient(const CXXNewExpr *NE,
295ffd83dbSDimitry Andric CheckerContext &C) const;
305ffd83dbSDimitry Andric
315ffd83dbSDimitry Andric bool checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
325ffd83dbSDimitry Andric CheckerContext &C) const;
335ffd83dbSDimitry Andric
34480093f4SDimitry Andric // Returns the size of the target in a placement new expression.
35480093f4SDimitry Andric // E.g. in "new (&s) long" it returns the size of `long`.
365ffd83dbSDimitry Andric SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, CheckerContext &C,
375ffd83dbSDimitry Andric bool &IsArray) const;
38480093f4SDimitry Andric // Returns the size of the place in a placement new expression.
39480093f4SDimitry Andric // E.g. in "new (&s) long" it returns the size of `s`.
405ffd83dbSDimitry Andric SVal getExtentSizeOfPlace(const CXXNewExpr *NE, CheckerContext &C) const;
415ffd83dbSDimitry Andric
425ffd83dbSDimitry Andric void emitBadAlignReport(const Expr *P, CheckerContext &C,
435ffd83dbSDimitry Andric unsigned AllocatedTAlign,
445ffd83dbSDimitry Andric unsigned StorageTAlign) const;
455ffd83dbSDimitry Andric unsigned getStorageAlign(CheckerContext &C, const ValueDecl *VD) const;
465ffd83dbSDimitry Andric
475ffd83dbSDimitry Andric void checkElementRegionAlign(const ElementRegion *R, CheckerContext &C,
485ffd83dbSDimitry Andric const Expr *P, unsigned AllocatedTAlign) const;
495ffd83dbSDimitry Andric
505ffd83dbSDimitry Andric void checkFieldRegionAlign(const FieldRegion *R, CheckerContext &C,
515ffd83dbSDimitry Andric const Expr *P, unsigned AllocatedTAlign) const;
525ffd83dbSDimitry Andric
535ffd83dbSDimitry Andric bool isVarRegionAlignedProperly(const VarRegion *R, CheckerContext &C,
545ffd83dbSDimitry Andric const Expr *P,
555ffd83dbSDimitry Andric unsigned AllocatedTAlign) const;
565ffd83dbSDimitry Andric
575ffd83dbSDimitry Andric BugType SBT{this, "Insufficient storage for placement new",
585ffd83dbSDimitry Andric categories::MemoryError};
595ffd83dbSDimitry Andric BugType ABT{this, "Bad align storage for placement new",
60480093f4SDimitry Andric categories::MemoryError};
61480093f4SDimitry Andric };
62480093f4SDimitry Andric } // namespace
63480093f4SDimitry Andric
getExtentSizeOfPlace(const CXXNewExpr * NE,CheckerContext & C) const645ffd83dbSDimitry Andric SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE,
65480093f4SDimitry Andric CheckerContext &C) const {
665ffd83dbSDimitry Andric const Expr *Place = NE->getPlacementArg(0);
67*fe6060f1SDimitry Andric return getDynamicExtentWithOffset(C.getState(), C.getSVal(Place));
68480093f4SDimitry Andric }
69480093f4SDimitry Andric
getExtentSizeOfNewTarget(const CXXNewExpr * NE,CheckerContext & C,bool & IsArray) const70480093f4SDimitry Andric SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
715ffd83dbSDimitry Andric CheckerContext &C,
725ffd83dbSDimitry Andric bool &IsArray) const {
735ffd83dbSDimitry Andric ProgramStateRef State = C.getState();
74480093f4SDimitry Andric SValBuilder &SvalBuilder = C.getSValBuilder();
75480093f4SDimitry Andric QualType ElementType = NE->getAllocatedType();
76480093f4SDimitry Andric ASTContext &AstContext = C.getASTContext();
77480093f4SDimitry Andric CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
785ffd83dbSDimitry Andric IsArray = false;
79480093f4SDimitry Andric if (NE->isArray()) {
805ffd83dbSDimitry Andric IsArray = true;
81480093f4SDimitry Andric const Expr *SizeExpr = *NE->getArraySize();
82480093f4SDimitry Andric SVal ElementCount = C.getSVal(SizeExpr);
83480093f4SDimitry Andric if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
84480093f4SDimitry Andric // size in Bytes = ElementCountNL * TypeSize
85480093f4SDimitry Andric return SvalBuilder.evalBinOp(
86480093f4SDimitry Andric State, BO_Mul, *ElementCountNL,
87480093f4SDimitry Andric SvalBuilder.makeArrayIndex(TypeSize.getQuantity()),
88480093f4SDimitry Andric SvalBuilder.getArrayIndexType());
89480093f4SDimitry Andric }
90480093f4SDimitry Andric } else {
91480093f4SDimitry Andric // Create a concrete int whose size in bits and signedness is equal to
92480093f4SDimitry Andric // ArrayIndexType.
93480093f4SDimitry Andric llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())
94480093f4SDimitry Andric .getQuantity() *
95480093f4SDimitry Andric C.getASTContext().getCharWidth(),
96480093f4SDimitry Andric TypeSize.getQuantity());
97480093f4SDimitry Andric return SvalBuilder.makeArrayIndex(I.getZExtValue());
98480093f4SDimitry Andric }
99480093f4SDimitry Andric return UnknownVal();
100480093f4SDimitry Andric }
101480093f4SDimitry Andric
checkPlaceCapacityIsSufficient(const CXXNewExpr * NE,CheckerContext & C) const1025ffd83dbSDimitry Andric bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
1035ffd83dbSDimitry Andric const CXXNewExpr *NE, CheckerContext &C) const {
1045ffd83dbSDimitry Andric bool IsArrayTypeAllocated;
1055ffd83dbSDimitry Andric SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, C, IsArrayTypeAllocated);
1065ffd83dbSDimitry Andric SVal SizeOfPlace = getExtentSizeOfPlace(NE, C);
1075ffd83dbSDimitry Andric const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
1085ffd83dbSDimitry Andric if (!SizeOfTargetCI)
1095ffd83dbSDimitry Andric return true;
1105ffd83dbSDimitry Andric const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
1115ffd83dbSDimitry Andric if (!SizeOfPlaceCI)
1125ffd83dbSDimitry Andric return true;
1135ffd83dbSDimitry Andric
1145ffd83dbSDimitry Andric if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||
1155ffd83dbSDimitry Andric (IsArrayTypeAllocated &&
1165ffd83dbSDimitry Andric SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {
1175ffd83dbSDimitry Andric if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
1185ffd83dbSDimitry Andric std::string Msg;
1195ffd83dbSDimitry Andric // TODO: use clang constant
1205ffd83dbSDimitry Andric if (IsArrayTypeAllocated &&
1215ffd83dbSDimitry Andric SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue())
1225ffd83dbSDimitry Andric Msg = std::string(llvm::formatv(
1235ffd83dbSDimitry Andric "{0} bytes is possibly not enough for array allocation which "
1245ffd83dbSDimitry Andric "requires {1} bytes. Current overhead requires the size of {2} "
1255ffd83dbSDimitry Andric "bytes",
1265ffd83dbSDimitry Andric SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(),
1275ffd83dbSDimitry Andric SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue()));
1285ffd83dbSDimitry Andric else if (IsArrayTypeAllocated &&
1295ffd83dbSDimitry Andric SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue())
1305ffd83dbSDimitry Andric Msg = std::string(llvm::formatv(
1315ffd83dbSDimitry Andric "Storage provided to placement new is only {0} bytes, "
1325ffd83dbSDimitry Andric "whereas the allocated array type requires more space for "
1335ffd83dbSDimitry Andric "internal needs",
1345ffd83dbSDimitry Andric SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
1355ffd83dbSDimitry Andric else
1365ffd83dbSDimitry Andric Msg = std::string(llvm::formatv(
1375ffd83dbSDimitry Andric "Storage provided to placement new is only {0} bytes, "
1385ffd83dbSDimitry Andric "whereas the allocated type requires {1} bytes",
1395ffd83dbSDimitry Andric SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
1405ffd83dbSDimitry Andric
1415ffd83dbSDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
1425ffd83dbSDimitry Andric bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);
1435ffd83dbSDimitry Andric C.emitReport(std::move(R));
1445ffd83dbSDimitry Andric
1455ffd83dbSDimitry Andric return false;
1465ffd83dbSDimitry Andric }
1475ffd83dbSDimitry Andric }
1485ffd83dbSDimitry Andric
1495ffd83dbSDimitry Andric return true;
1505ffd83dbSDimitry Andric }
1515ffd83dbSDimitry Andric
emitBadAlignReport(const Expr * P,CheckerContext & C,unsigned AllocatedTAlign,unsigned StorageTAlign) const1525ffd83dbSDimitry Andric void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C,
1535ffd83dbSDimitry Andric unsigned AllocatedTAlign,
1545ffd83dbSDimitry Andric unsigned StorageTAlign) const {
1555ffd83dbSDimitry Andric ProgramStateRef State = C.getState();
1565ffd83dbSDimitry Andric if (ExplodedNode *N = C.generateErrorNode(State)) {
1575ffd83dbSDimitry Andric std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but "
1585ffd83dbSDimitry Andric "allocated type is aligned to {1} bytes",
1595ffd83dbSDimitry Andric StorageTAlign, AllocatedTAlign));
1605ffd83dbSDimitry Andric
1615ffd83dbSDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);
1625ffd83dbSDimitry Andric bugreporter::trackExpressionValue(N, P, *R);
1635ffd83dbSDimitry Andric C.emitReport(std::move(R));
1645ffd83dbSDimitry Andric }
1655ffd83dbSDimitry Andric }
1665ffd83dbSDimitry Andric
getStorageAlign(CheckerContext & C,const ValueDecl * VD) const1675ffd83dbSDimitry Andric unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C,
1685ffd83dbSDimitry Andric const ValueDecl *VD) const {
1695ffd83dbSDimitry Andric unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType());
1705ffd83dbSDimitry Andric if (unsigned SpecifiedAlignment = VD->getMaxAlignment())
1715ffd83dbSDimitry Andric StorageTAlign = SpecifiedAlignment;
1725ffd83dbSDimitry Andric
1735ffd83dbSDimitry Andric return StorageTAlign / C.getASTContext().getCharWidth();
1745ffd83dbSDimitry Andric }
1755ffd83dbSDimitry Andric
checkElementRegionAlign(const ElementRegion * R,CheckerContext & C,const Expr * P,unsigned AllocatedTAlign) const1765ffd83dbSDimitry Andric void PlacementNewChecker::checkElementRegionAlign(
1775ffd83dbSDimitry Andric const ElementRegion *R, CheckerContext &C, const Expr *P,
1785ffd83dbSDimitry Andric unsigned AllocatedTAlign) const {
1795ffd83dbSDimitry Andric auto IsBaseRegionAlignedProperly = [this, R, &C, P,
1805ffd83dbSDimitry Andric AllocatedTAlign]() -> bool {
1815ffd83dbSDimitry Andric // Unwind nested ElementRegion`s to get the type.
1825ffd83dbSDimitry Andric const MemRegion *SuperRegion = R;
1835ffd83dbSDimitry Andric while (true) {
1845ffd83dbSDimitry Andric if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {
1855ffd83dbSDimitry Andric SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();
1865ffd83dbSDimitry Andric continue;
1875ffd83dbSDimitry Andric }
1885ffd83dbSDimitry Andric
1895ffd83dbSDimitry Andric break;
1905ffd83dbSDimitry Andric }
1915ffd83dbSDimitry Andric
1925ffd83dbSDimitry Andric const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();
1935ffd83dbSDimitry Andric if (!TheElementDeclRegion)
1945ffd83dbSDimitry Andric return false;
1955ffd83dbSDimitry Andric
1965ffd83dbSDimitry Andric const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();
1975ffd83dbSDimitry Andric if (!BaseDeclRegion)
1985ffd83dbSDimitry Andric return false;
1995ffd83dbSDimitry Andric
2005ffd83dbSDimitry Andric unsigned BaseRegionAlign = 0;
2015ffd83dbSDimitry Andric // We must use alignment TheElementDeclRegion if it has its own alignment
2025ffd83dbSDimitry Andric // specifier
2035ffd83dbSDimitry Andric if (TheElementDeclRegion->getDecl()->getMaxAlignment())
2045ffd83dbSDimitry Andric BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());
2055ffd83dbSDimitry Andric else
2065ffd83dbSDimitry Andric BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());
2075ffd83dbSDimitry Andric
2085ffd83dbSDimitry Andric if (AllocatedTAlign > BaseRegionAlign) {
2095ffd83dbSDimitry Andric emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign);
2105ffd83dbSDimitry Andric return false;
2115ffd83dbSDimitry Andric }
2125ffd83dbSDimitry Andric
2135ffd83dbSDimitry Andric return true;
2145ffd83dbSDimitry Andric };
2155ffd83dbSDimitry Andric
2165ffd83dbSDimitry Andric auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void {
2175ffd83dbSDimitry Andric RegionOffset TheOffsetRegion = R->getAsOffset();
2185ffd83dbSDimitry Andric if (TheOffsetRegion.hasSymbolicOffset())
2195ffd83dbSDimitry Andric return;
2205ffd83dbSDimitry Andric
2215ffd83dbSDimitry Andric unsigned Offset =
2225ffd83dbSDimitry Andric TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth();
2235ffd83dbSDimitry Andric unsigned AddressAlign = Offset % AllocatedTAlign;
2245ffd83dbSDimitry Andric if (AddressAlign != 0) {
2255ffd83dbSDimitry Andric emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
2265ffd83dbSDimitry Andric return;
2275ffd83dbSDimitry Andric }
2285ffd83dbSDimitry Andric };
2295ffd83dbSDimitry Andric
2305ffd83dbSDimitry Andric if (IsBaseRegionAlignedProperly()) {
2315ffd83dbSDimitry Andric CheckElementRegionOffset();
2325ffd83dbSDimitry Andric }
2335ffd83dbSDimitry Andric }
2345ffd83dbSDimitry Andric
checkFieldRegionAlign(const FieldRegion * R,CheckerContext & C,const Expr * P,unsigned AllocatedTAlign) const2355ffd83dbSDimitry Andric void PlacementNewChecker::checkFieldRegionAlign(
2365ffd83dbSDimitry Andric const FieldRegion *R, CheckerContext &C, const Expr *P,
2375ffd83dbSDimitry Andric unsigned AllocatedTAlign) const {
2385ffd83dbSDimitry Andric const MemRegion *BaseRegion = R->getBaseRegion();
2395ffd83dbSDimitry Andric if (!BaseRegion)
2405ffd83dbSDimitry Andric return;
2415ffd83dbSDimitry Andric
2425ffd83dbSDimitry Andric if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) {
2435ffd83dbSDimitry Andric if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) {
2445ffd83dbSDimitry Andric // We've checked type align but, unless FieldRegion
2455ffd83dbSDimitry Andric // offset is zero, we also need to check its own
2465ffd83dbSDimitry Andric // align.
2475ffd83dbSDimitry Andric RegionOffset Offset = R->getAsOffset();
2485ffd83dbSDimitry Andric if (Offset.hasSymbolicOffset())
2495ffd83dbSDimitry Andric return;
2505ffd83dbSDimitry Andric
2515ffd83dbSDimitry Andric int64_t OffsetValue =
2525ffd83dbSDimitry Andric Offset.getOffset() / C.getASTContext().getCharWidth();
2535ffd83dbSDimitry Andric unsigned AddressAlign = OffsetValue % AllocatedTAlign;
2545ffd83dbSDimitry Andric if (AddressAlign != 0)
2555ffd83dbSDimitry Andric emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
2565ffd83dbSDimitry Andric }
2575ffd83dbSDimitry Andric }
2585ffd83dbSDimitry Andric }
2595ffd83dbSDimitry Andric
isVarRegionAlignedProperly(const VarRegion * R,CheckerContext & C,const Expr * P,unsigned AllocatedTAlign) const2605ffd83dbSDimitry Andric bool PlacementNewChecker::isVarRegionAlignedProperly(
2615ffd83dbSDimitry Andric const VarRegion *R, CheckerContext &C, const Expr *P,
2625ffd83dbSDimitry Andric unsigned AllocatedTAlign) const {
2635ffd83dbSDimitry Andric const VarDecl *TheVarDecl = R->getDecl();
2645ffd83dbSDimitry Andric unsigned StorageTAlign = getStorageAlign(C, TheVarDecl);
2655ffd83dbSDimitry Andric if (AllocatedTAlign > StorageTAlign) {
2665ffd83dbSDimitry Andric emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign);
2675ffd83dbSDimitry Andric
2685ffd83dbSDimitry Andric return false;
2695ffd83dbSDimitry Andric }
2705ffd83dbSDimitry Andric
2715ffd83dbSDimitry Andric return true;
2725ffd83dbSDimitry Andric }
2735ffd83dbSDimitry Andric
checkPlaceIsAlignedProperly(const CXXNewExpr * NE,CheckerContext & C) const2745ffd83dbSDimitry Andric bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
2755ffd83dbSDimitry Andric CheckerContext &C) const {
2765ffd83dbSDimitry Andric const Expr *Place = NE->getPlacementArg(0);
2775ffd83dbSDimitry Andric
2785ffd83dbSDimitry Andric QualType AllocatedT = NE->getAllocatedType();
2795ffd83dbSDimitry Andric unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) /
2805ffd83dbSDimitry Andric C.getASTContext().getCharWidth();
2815ffd83dbSDimitry Andric
2825ffd83dbSDimitry Andric SVal PlaceVal = C.getSVal(Place);
2835ffd83dbSDimitry Andric if (const MemRegion *MRegion = PlaceVal.getAsRegion()) {
2845ffd83dbSDimitry Andric if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>())
2855ffd83dbSDimitry Andric checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign);
2865ffd83dbSDimitry Andric else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>())
2875ffd83dbSDimitry Andric checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign);
2885ffd83dbSDimitry Andric else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>())
2895ffd83dbSDimitry Andric isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign);
2905ffd83dbSDimitry Andric }
2915ffd83dbSDimitry Andric
2925ffd83dbSDimitry Andric return true;
2935ffd83dbSDimitry Andric }
2945ffd83dbSDimitry Andric
checkPreStmt(const CXXNewExpr * NE,CheckerContext & C) const295480093f4SDimitry Andric void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
296480093f4SDimitry Andric CheckerContext &C) const {
297480093f4SDimitry Andric // Check only the default placement new.
298480093f4SDimitry Andric if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
299480093f4SDimitry Andric return;
3005ffd83dbSDimitry Andric
301480093f4SDimitry Andric if (NE->getNumPlacementArgs() == 0)
302480093f4SDimitry Andric return;
303480093f4SDimitry Andric
3045ffd83dbSDimitry Andric if (!checkPlaceCapacityIsSufficient(NE, C))
305480093f4SDimitry Andric return;
306480093f4SDimitry Andric
3075ffd83dbSDimitry Andric checkPlaceIsAlignedProperly(NE, C);
308480093f4SDimitry Andric }
309480093f4SDimitry Andric
registerPlacementNewChecker(CheckerManager & mgr)310480093f4SDimitry Andric void ento::registerPlacementNewChecker(CheckerManager &mgr) {
311480093f4SDimitry Andric mgr.registerChecker<PlacementNewChecker>();
312480093f4SDimitry Andric }
313480093f4SDimitry Andric
shouldRegisterPlacementNewChecker(const CheckerManager & mgr)3145ffd83dbSDimitry Andric bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {
315480093f4SDimitry Andric return true;
316480093f4SDimitry Andric }
317