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 + PlacementNewTargetTypeMismatch.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 "PlacementNewTargetTypeMismatch.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-type-mismatch"); CheckFactories.registerCheck( "misc-redundant-expression"); CheckFactories.registerCheck("misc-static-assert"); Index: clang-tidy/misc/PlacementNewTargetTypeMismatch.h =================================================================== --- /dev/null +++ clang-tidy/misc/PlacementNewTargetTypeMismatch.h @@ -0,0 +1,36 @@ +//===--- 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_MISC_PLACEMENTNEWTARGETTYPEMISMATCHCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_PLACEMENTNEWTARGETTYPEMISMATCHCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// 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/misc-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 misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_PLACEMENTNEWTARGETTYPEMISMATCHCHECK_H Index: clang-tidy/misc/PlacementNewTargetTypeMismatch.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/PlacementNewTargetTypeMismatch.cpp @@ -0,0 +1,61 @@ +//===--- 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 misc { + +void PlacementNewTargetTypeMismatch::registerMatchers(MatchFinder *Finder) { + // We only want the records that call 'new' with an adress parameter + Finder->addMatcher( + cxxNewExpr(hasDescendant(implicitCastExpr().bind("Cast"))).bind("Alloc"), + this); +} + +void PlacementNewTargetTypeMismatch::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; + } + + const Type *SourceType = + Alloc->getAllocatedType()->getUnqualifiedDesugaredType(); + const ImplicitCastExpr *Cast = + Result.Nodes.getNodeAs("Cast"); + + const QualType &CastType = Cast->getSubExpr()->getType(); + const Type *TargetType = + CastType->isPointerType() + ? CastType->getPointeeType()->getUnqualifiedDesugaredType() + : CastType->getUnqualifiedDesugaredType(); + + if (!SourceType || !TargetType) { + // shall never happen + return; + } + + if (SourceType != TargetType) { + diag(Alloc->getExprLoc(), "placement new of incompatible types"); + } +} + +} // 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-type-mismatch + ` 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-type-mismatch misc-redundant-expression misc-static-assert misc-throw-by-value-catch-by-reference Index: docs/clang-tidy/checks/misc-placement-new-target-type-mismatch.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-placement-new-target-type-mismatch.rst @@ -0,0 +1,8 @@ +.. 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. + Index: test/clang-tidy/misc-placement-new-target-type-mismatch.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-placement-new-target-type-mismatch.cpp @@ -0,0 +1,82 @@ +// RUN: %check_clang_tidy %s misc-placement-new-target-type-mismatch %t + +// definitions + +using size_type = unsigned long; +void *operator new(size_type, void *); +void *operator new[](size_type, void *); + +namespace std { +template T* addressof(T& arg) noexcept; +} // namespace std + +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]]:3: warning: placement new of incompatible types [misc-placement-new-target-type-mismatch] + delete ptr; +} + +void f2() { + int * ptr = new int; + new (ptr) Foo; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: placement new of incompatible types [misc-placement-new-target-type-mismatch] + delete ptr; +} + +void f3() { + char *ptr = new char[17*sizeof(char)]; + new(ptr) float[13]; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: placement new of incompatible types [misc-placement-new-target-type-mismatch] + delete[] ptr; +} + +void f4() { + new (std::addressof(getT())) Foo; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: placement new of incompatible types [misc-placement-new-target-type-mismatch] +} + +void f5() { + char *ptr = new char[17*sizeof(char)]; + new(ptr) float{13.f}; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: placement new of incompatible types [misc-placement-new-target-type-mismatch] + delete[] ptr; +} + +// instances not emitting a warning + +void f6() { + Foo * ptr = new Foo; + new (ptr) Foo; + delete ptr; +} + +void f7() { + new ((void *)std::addressof(getT())) Foo; +} + +void f8() { + char *ptr = new char[17*sizeof(char)]; + new((float *)ptr) float{13.f}; + + delete[] ptr; +} \ No newline at end of file