Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -7,6 +7,7 @@ NewDeleteOverloadsCheck.cpp NonCopyableObjects.cpp NonPrivateMemberVariablesInClassesCheck.cpp + PlacementNewTargetSizeCheck.cpp RedundantExpressionCheck.cpp StaticAssertCheck.cpp ThrowByValueCatchByReferenceCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -14,6 +14,7 @@ #include "NewDeleteOverloadsCheck.h" #include "NonCopyableObjects.h" #include "NonPrivateMemberVariablesInClassesCheck.h" +#include "PlacementNewTargetSizeCheck.h" #include "RedundantExpressionCheck.h" #include "StaticAssertCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" @@ -39,6 +40,8 @@ "misc-non-copyable-objects"); CheckFactories.registerCheck( "misc-non-private-member-variables-in-classes"); + CheckFactories.registerCheck( + "misc-placement-new-target-size"); CheckFactories.registerCheck( "misc-redundant-expression"); CheckFactories.registerCheck("misc-static-assert"); Index: clang-tidy/misc/PlacementNewTargetSizeCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/PlacementNewTargetSizeCheck.h @@ -0,0 +1,37 @@ +//===--- PlacementNewTargetSizeCheck.h - clang-tidy -------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_PLACEMENTNEWTARGETSIZECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_PLACEMENTNEWTARGETSIZECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// This checker finds placement new statements that construct objects into +/// allocated space where the pointer to that allocated space is of a type +/// smaller than the constructed object and the pointer is implicitly cast to +/// void *. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-placement-new-target-size.html +class PlacementNewTargetSizeCheck : public ClangTidyCheck { +public: + PlacementNewTargetSizeCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_PLACEMENTNEWTARGETSIZECHECK_H Index: clang-tidy/misc/PlacementNewTargetSizeCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/PlacementNewTargetSizeCheck.cpp @@ -0,0 +1,78 @@ +//===--- PlacementNewTargetSizeCheck.cpp - clang-tidy ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "PlacementNewTargetSizeCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +static CharUnits getSizeOfType(clang::ASTContext *const Context, + const QualType &type) { + CharUnits TypeSize; + if (type->isRecordType()) { + const RecordDecl *CXXRecordDecl = type->getAsRecordDecl(); + assert(CXXRecordDecl && "type->getAsRecordDecl() failed"); + const ASTRecordLayout &CXXDeclLayout = + CXXRecordDecl->getASTContext().getASTRecordLayout(CXXRecordDecl); + TypeSize = CXXDeclLayout.getDataSize(); + } else if (type->isBuiltinType()) { + TypeSize = Context->getTypeSizeInChars(type); + } else { + // TODO: handle constructing into C-style arrays + } + return TypeSize; +} + +void PlacementNewTargetSizeCheck::registerMatchers(MatchFinder *Finder) { + // We only want the records that call 'new' with a pointer that is implicitly + // cast into void *. + Finder->addMatcher( + cxxNewExpr(hasDescendant(implicitCastExpr().bind("Cast"))).bind("Alloc"), + this); +} + +void PlacementNewTargetSizeCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *Alloc = Result.Nodes.getNodeAs("Alloc"); + assert(Alloc && "Matched node bound by 'Alloc' shoud be a 'CXXNewExpr'"); + + unsigned numPlacementArgs = Alloc->getNumPlacementArgs(); + if (0 == numPlacementArgs) { + return; + } + + QualType type = Alloc->getAllocatedType(); + CharUnits TypeSize = getSizeOfType(Result.Context, type); + if (TypeSize.isZero()) { + return; + } + + // get ptr type of implicit cast + const ImplicitCastExpr *Cast = + Result.Nodes.getNodeAs("Cast"); + QualType CastType = Cast->getSubExpr()->getType(); + if (CastType->isPointerType()) { + CharUnits CastSize = + getSizeOfType(Result.Context, CastType->getPointeeType()); + if ((TypeSize - CastSize).isPositive()) { + diag(Alloc->getExprLoc(), "placement new has insufficient target size"); + } + } else { + // TODO: handle constructing into C-style arrays + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -130,6 +130,13 @@ ` now supports `OverrideSpelling` and `FinalSpelling` options. +- New :doc:`misc-placement-new-target-size + ` check. + + Finds placement-new calls where the size of the pointee type of the placement + parameter is smaller than the size of the constructed type and the pointer is + implicitly cast to ``void *``. + - New :doc:`openmp-exception-escape ` check. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -184,6 +184,7 @@ misc-new-delete-overloads misc-non-copyable-objects misc-non-private-member-variables-in-classes + misc-placement-new-target-size misc-redundant-expression misc-static-assert misc-throw-by-value-catch-by-reference Index: docs/clang-tidy/checks/misc-placement-new-target-size.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-placement-new-target-size.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - misc-placement-new-target-size + +misc-placement-new-target-size +============================== + + +Finds placement-new calls where the size of the pointee type of the placement +parameter is smaller than the size of the constructed type and the pointer is +implicitly cast to ``void *``. + Index: test/clang-tidy/misc-placement-new-target-size.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-placement-new-target-size.cpp @@ -0,0 +1,15 @@ +// RUN: %check_clang_tidy %s misc-placement-new-target-size %t + +using size_type = unsigned long; +void *operator new(size_type, void *); + +void f() { + struct Dummy { + int a; + int b; + }; + int *ptr = new int; + new (ptr) Dummy; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: placement new has insufficient target size [misc-placement-new-target-size] + delete ptr; +}