Index: clang-tidy/bugprone/BugproneTidyModule.cpp =================================================================== --- clang-tidy/bugprone/BugproneTidyModule.cpp +++ clang-tidy/bugprone/BugproneTidyModule.cpp @@ -30,6 +30,7 @@ #include "MoveForwardingReferenceCheck.h" #include "MultipleStatementMacroCheck.h" #include "ParentVirtualCallCheck.h" +#include "PlacementNewTargetTypeMismatch.h" #include "SizeofContainerCheck.h" #include "SizeofExpressionCheck.h" #include "StringConstructorCheck.h" @@ -102,6 +103,8 @@ "bugprone-narrowing-conversions"); CheckFactories.registerCheck( "bugprone-parent-virtual-call"); + CheckFactories.registerCheck( + "bugprone-placement-new-target-type-mismatch"); CheckFactories.registerCheck( "bugprone-sizeof-container"); CheckFactories.registerCheck( Index: clang-tidy/bugprone/CMakeLists.txt =================================================================== --- clang-tidy/bugprone/CMakeLists.txt +++ clang-tidy/bugprone/CMakeLists.txt @@ -22,6 +22,7 @@ MoveForwardingReferenceCheck.cpp MultipleStatementMacroCheck.cpp ParentVirtualCallCheck.cpp + PlacementNewTargetTypeMismatch.cpp SizeofContainerCheck.cpp SizeofExpressionCheck.cpp StringConstructorCheck.cpp Index: clang-tidy/bugprone/PlacementNewTargetTypeMismatch.h =================================================================== --- /dev/null +++ clang-tidy/bugprone/PlacementNewTargetTypeMismatch.h @@ -0,0 +1,35 @@ +//===--- PlacementNewTargetTypeMismatch.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_BUGPRONE_PLACEMENTNEWTARGETTYPEMISMATCHCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PLACEMENTNEWTARGETTYPEMISMATCHCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Finds placement-new calls where the pointer type of the adress mismatches +/// the type of the created value. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-placement-new-target-type-mismatch.html +class PlacementNewTargetTypeMismatch : public ClangTidyCheck { +public: + PlacementNewTargetTypeMismatch(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PLACEMENTNEWTARGETTYPEMISMATCHCHECK_H Index: clang-tidy/bugprone/PlacementNewTargetTypeMismatch.cpp =================================================================== --- /dev/null +++ clang-tidy/bugprone/PlacementNewTargetTypeMismatch.cpp @@ -0,0 +1,69 @@ +//===--- PlacementNewTargetTypeMismatch.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 "PlacementNewTargetTypeMismatch.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 bugprone { +namespace { +AST_MATCHER(Expr, isPlacementNewExpr) { + const auto *NewExpr = dyn_cast(&Node); + return NewExpr && NewExpr->getNumPlacementArgs() != 0; +} +} // namespace + +void PlacementNewTargetTypeMismatch::registerMatchers(MatchFinder *Finder) { + // We only want the records that call 'new' with an adress parameter. + Finder->addMatcher( + expr(isPlacementNewExpr(), unless(hasDescendant(unresolvedLookupExpr()))) + .bind("NewExpr"), + this); +} + +void PlacementNewTargetTypeMismatch::check( + const MatchFinder::MatchResult &Result) { + const auto *NewExpr = Result.Nodes.getNodeAs("NewExpr"); + assert(NewExpr && "Matched node bound by 'NewExpr' shoud be a 'CXXNewExpr'"); + assert(NewExpr->getNumPlacementArgs() != 0 && ""); + + // Fetch the cast from the Expr of the placement argument, if it exists. + const Expr *PlacementExpr = NewExpr->getPlacementArg(0); + assert(PlacementExpr != nullptr && "PlacementExpr should not be null"); + const CastExpr *Cast = dyn_cast(PlacementExpr); + if (Cast == nullptr) + return; + + QualType SubExprType = Cast->getSubExprAsWritten()->getType(); + + if ((!SubExprType->isPointerType() && !SubExprType->isArrayType()) || + SubExprType->isVoidPointerType()) + return; + + QualType PlacementParameterType = + SubExprType->getPointeeOrArrayElementType()->getCanonicalTypeInternal(); + QualType AllocatedType = NewExpr->getAllocatedType().getCanonicalType(); + + if (AllocatedType->isIncompleteType() || + PlacementParameterType->isIncompleteType()) + return; + + if (PlacementParameterType != AllocatedType) { + diag(PlacementExpr->getExprLoc(), + "placement new parameter and allocated type mismatch"); + } +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -130,6 +130,12 @@ ` now supports `OverrideSpelling` and `FinalSpelling` options. +- New :doc:`bugprone-placement-new-target-type-mismatch + ` check. + + Finds placement-new calls where the pointer type of the adress mismatches the + type of the created value. + - New :doc:`openmp-exception-escape ` check. Index: docs/clang-tidy/checks/bugprone-placement-new-target-type-mismatch.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/bugprone-placement-new-target-type-mismatch.rst @@ -0,0 +1,23 @@ +.. title:: clang-tidy - misc-placement-new-target-type-mismatch + +misc-placement-new-target-type-mismatch +======================================= + +Finds placement-new calls where the pointer type of the adress mismatches the +type of the created value. + +Example: + +.. code-block:: c++ + + void ok() { + Foo * ptr = new Foo; + new (ptr) Foo; + ... + } + + void not_ok() { + int * ptr = new int; + new (ptr) Foo; + ... + } Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -55,6 +55,7 @@ bugprone-move-forwarding-reference bugprone-multiple-statement-macro bugprone-parent-virtual-call + bugprone-placement-new-target-type-mismatch bugprone-sizeof-container bugprone-sizeof-expression bugprone-string-constructor Index: test/clang-tidy/bugprone-placement-new-target-type-mismatch.cpp =================================================================== --- /dev/null +++ test/clang-tidy/bugprone-placement-new-target-type-mismatch.cpp @@ -0,0 +1,118 @@ +// RUN: %check_clang_tidy %s bugprone-placement-new-target-type-mismatch %t + +// definitions + +namespace std { +struct nothrow_t { explicit nothrow_t() = default; } nothrow; +template T* addressof(T& arg) noexcept; +template< class T > struct remove_reference {typedef T type;}; +template< class T > struct remove_reference {typedef T type;}; +template< class T > struct remove_reference {typedef T type;}; +template< class T > +T&& forward( typename std::remove_reference::type& t ) noexcept; +} // namespace std + +using size_type = unsigned long; +void *operator new(size_type, void *); +void *operator new[](size_type, void *); +void* operator new(size_type size, const std::nothrow_t&) noexcept; +void* operator new(size_type size, const std::nothrow_t&) noexcept; +void* operator new[](size_type size, const std::nothrow_t&) noexcept; + +struct Foo { + int a; + int b; + int c; + int d; +}; + +template +T& getT() { + static T f; + return f; +} + +// instances emitting warnings + +void f1() { + struct Dummy { + int a; + int b; + }; + int *ptr = new int; + new (ptr) Dummy; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: placement new parameter and allocated type mismatch [bugprone-placement-new-target-type-mismatch] +} + +void f2() { + int * ptr = new int; + new (ptr) Foo; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: placement new parameter and allocated type mismatch [bugprone-placement-new-target-type-mismatch] +} + +void f3() { + char *ptr = new char[17*sizeof(char)]; + new (ptr) float[13]; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: placement new parameter and allocated type mismatch [bugprone-placement-new-target-type-mismatch] +} + +void f4() { + new (std::addressof(getT())) Foo; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: placement new parameter and allocated type mismatch [bugprone-placement-new-target-type-mismatch] +} + +void f5() { + char *ptr = new char[17*sizeof(char)]; + new (ptr) float{13.f}; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: placement new parameter and allocated type mismatch [bugprone-placement-new-target-type-mismatch] +} + +void f6() { + char array[17*sizeof(char)]; + new (array) float{13.f}; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: placement new parameter and allocated type mismatch [bugprone-placement-new-target-type-mismatch] +} + +// instances not emitting a warning + +void g1() { + Foo * ptr = new Foo; + new (ptr) Foo; +} + +void g2() { + char *ptr = new char[17*sizeof(char)]; + new ((float *)ptr) float{13.f}; +} + +void g3() { + char array[17*sizeof(char)]; + new (array) char('A'); +} + +void g4() { + new ((void *)std::addressof(getT())) Foo; +} + +union +{ + char * buffer; +} Union; + +template +void g5(U &&... V) { + new (static_cast(Union.buffer)) T(std::forward(V)...); +} + +template +void g6(U &&... V) { + new (std::nothrow) T(std::forward(V)...); +} + +template void g7(U &&... Us) { + new (static_cast(Union.buffer)) T(std::forward(Us)...); +} + +template void g8(U &&... Us) { + new (Union.buffer) T(std::forward(Us)...); +}