Skip to content

Commit

Permalink
[clang-tidy] Add new check 'shared-ptr-array-mismatch'.
Browse files Browse the repository at this point in the history
Reviewed By: LegalizeAdulthood

Differential Revision: https://reviews.llvm.org/D117306
  • Loading branch information
balazske committed Feb 7, 2022
1 parent 9576698 commit c63522e
Show file tree
Hide file tree
Showing 10 changed files with 378 additions and 0 deletions.
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
Expand Up @@ -42,6 +42,7 @@
#include "PosixReturnCheck.h"
#include "RedundantBranchConditionCheck.h"
#include "ReservedIdentifierCheck.h"
#include "SharedPtrArrayMismatchCheck.h"
#include "SignalHandlerCheck.h"
#include "SignedCharMisuseCheck.h"
#include "SizeofContainerCheck.h"
Expand Down Expand Up @@ -143,6 +144,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-posix-return");
CheckFactories.registerCheck<ReservedIdentifierCheck>(
"bugprone-reserved-identifier");
CheckFactories.registerCheck<SharedPtrArrayMismatchCheck>(
"bugprone-shared-ptr-array-mismatch");
CheckFactories.registerCheck<SignalHandlerCheck>("bugprone-signal-handler");
CheckFactories.registerCheck<SignedCharMisuseCheck>(
"bugprone-signed-char-misuse");
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
Expand Up @@ -37,6 +37,8 @@ add_clang_library(clangTidyBugproneModule
PosixReturnCheck.cpp
RedundantBranchConditionCheck.cpp
ReservedIdentifierCheck.cpp
SharedPtrArrayMismatchCheck.cpp
SmartPtrArrayMismatchCheck.cpp
SignalHandlerCheck.cpp
SignedCharMisuseCheck.cpp
SizeofContainerCheck.cpp
Expand Down
@@ -0,0 +1,31 @@
//===--- SharedPtrArrayMismatchCheck.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 "SharedPtrArrayMismatchCheck.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace bugprone {

SharedPtrArrayMismatchCheck::SharedPtrArrayMismatchCheck(
StringRef Name, ClangTidyContext *Context)
: SmartPtrArrayMismatchCheck(Name, Context, "shared") {}

SharedPtrArrayMismatchCheck::SmartPtrClassMatcher
SharedPtrArrayMismatchCheck::getSmartPointerClassMatcher() const {
return classTemplateSpecializationDecl(
hasName("::std::shared_ptr"), templateArgumentCountIs(1),
hasTemplateArgument(
0, templateArgument(refersToType(qualType().bind(PointerTypeN)))));
}

} // namespace bugprone
} // namespace tidy
} // namespace clang
@@ -0,0 +1,38 @@
//===--- SharedPtrArrayMismatchCheck.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_SHAREDPTRARRAYMISMATCHCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SHAREDPTRARRAYMISMATCHCHECK_H

#include "SmartPtrArrayMismatchCheck.h"

namespace clang {
namespace tidy {
namespace bugprone {

/// Find `std::shared_ptr<T>(new T[...])`, replace it (if applicable) with
/// `std::shared_ptr<T[]>(new T[...])`.
///
/// Example:
///
/// \code
/// std::shared_ptr<int> PtrArr{new int[10]};
/// \endcode
class SharedPtrArrayMismatchCheck : public SmartPtrArrayMismatchCheck {
public:
SharedPtrArrayMismatchCheck(StringRef Name, ClangTidyContext *Context);

protected:
virtual SmartPtrClassMatcher getSmartPointerClassMatcher() const;
};

} // namespace bugprone
} // namespace tidy
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SHAREDPTRARRAYMISMATCHCHECK_H
121 changes: 121 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/SmartPtrArrayMismatchCheck.cpp
@@ -0,0 +1,121 @@
//===--- SmartPtrArrayMismatchCheck.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 "SmartPtrArrayMismatchCheck.h"
#include "../utils/ASTUtils.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace bugprone {

namespace {

constexpr char ConstructExprN[] = "found_construct_expr";
constexpr char NewExprN[] = "found_new_expr";
constexpr char ConstructorN[] = "found_constructor";

bool isInSingleDeclStmt(const DeclaratorDecl *D) {
const DynTypedNodeList Parents =
D->getASTContext().getParentMapContext().getParents(*D);
for (const DynTypedNode &PNode : Parents)
if (const auto *PDecl = PNode.get<DeclStmt>())
return PDecl->isSingleDecl();
return false;
}

const DeclaratorDecl *getConstructedVarOrField(const Expr *FoundConstructExpr,
ASTContext &Ctx) {
const DynTypedNodeList ConstructParents =
Ctx.getParentMapContext().getParents(*FoundConstructExpr);
if (ConstructParents.size() != 1)
return nullptr;
const auto *ParentDecl = ConstructParents.begin()->get<DeclaratorDecl>();
if (isa_and_nonnull<VarDecl, FieldDecl>(ParentDecl))
return ParentDecl;

return nullptr;
}

} // namespace

const char SmartPtrArrayMismatchCheck::PointerTypeN[] = "pointer_type";

SmartPtrArrayMismatchCheck::SmartPtrArrayMismatchCheck(
StringRef Name, ClangTidyContext *Context, StringRef SmartPointerName)
: ClangTidyCheck(Name, Context), SmartPointerName(SmartPointerName) {}

void SmartPtrArrayMismatchCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {}

void SmartPtrArrayMismatchCheck::registerMatchers(MatchFinder *Finder) {
// For both shared and unique pointers, we need to find constructor with
// exactly one parameter that has the pointer type. Other constructors are
// not applicable for this check.
auto FindConstructor =
cxxConstructorDecl(ofClass(getSmartPointerClassMatcher()),
parameterCountIs(1), isExplicit())
.bind(ConstructorN);
auto FindConstructExpr =
cxxConstructExpr(
hasDeclaration(FindConstructor), argumentCountIs(1),
hasArgument(
0, cxxNewExpr(isArray(), hasType(pointerType(pointee(
equalsBoundNode(PointerTypeN)))))
.bind(NewExprN)))
.bind(ConstructExprN);
Finder->addMatcher(FindConstructExpr, this);
}

void SmartPtrArrayMismatchCheck::check(const MatchFinder::MatchResult &Result) {
const auto *FoundNewExpr = Result.Nodes.getNodeAs<CXXNewExpr>(NewExprN);
const auto *FoundConstructExpr =
Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructExprN);
const auto *FoundConstructorDecl =
Result.Nodes.getNodeAs<CXXConstructorDecl>(ConstructorN);

ASTContext &Ctx = FoundConstructorDecl->getASTContext();
const DeclaratorDecl *VarOrField =
getConstructedVarOrField(FoundConstructExpr, Ctx);

auto D = diag(FoundNewExpr->getBeginLoc(),
"%0 pointer to non-array is initialized with array")
<< SmartPointerName;
D << FoundNewExpr->getSourceRange();

if (VarOrField) {
auto TSTypeLoc = VarOrField->getTypeSourceInfo()
->getTypeLoc()
.getAsAdjusted<clang::TemplateSpecializationTypeLoc>();
assert(TSTypeLoc.getNumArgs() >= 1 &&
"Matched type should have at least 1 template argument.");

SourceRange TemplateArgumentRange = TSTypeLoc.getArgLoc(0)
.getTypeSourceInfo()
->getTypeLoc()
.getLocalSourceRange();
D << TemplateArgumentRange;

if (isInSingleDeclStmt(VarOrField)) {
const SourceManager &SM = Ctx.getSourceManager();
if (!utils::rangeCanBeFixed(TemplateArgumentRange, &SM))
return;

SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
TemplateArgumentRange.getEnd(), 0, SM, Ctx.getLangOpts());
D << FixItHint::CreateInsertion(InsertLoc, "[]");
}
}
}

} // namespace bugprone
} // namespace tidy
} // namespace clang
52 changes: 52 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/SmartPtrArrayMismatchCheck.h
@@ -0,0 +1,52 @@
//===--- SharedPtrArrayMismatchCheck.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_SMARTPTRARRAYMISMATCHCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SMARTPTRARRAYMISMATCHCHECK_H

#include "../ClangTidyCheck.h"

namespace clang {
namespace tidy {
namespace bugprone {

/// Find constructions of smart (unique or shared) pointers where the pointer
/// is declared with non-array target type and an array (created with a
/// new-expression) is passed to it.
class SmartPtrArrayMismatchCheck : public ClangTidyCheck {
public:
SmartPtrArrayMismatchCheck(StringRef Name, ClangTidyContext *Context,
StringRef SmartPointerName);

bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus11;
}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;

protected:
using SmartPtrClassMatcher = ast_matchers::internal::BindableMatcher<Decl>;

/// Returns matcher that match with different smart pointer classes.
///
/// Requires to bind pointer type (qualType) with PointerTypeN string declared
/// in this class.
virtual SmartPtrClassMatcher getSmartPointerClassMatcher() const = 0;

static const char PointerTypeN[];

private:
StringRef const SmartPointerName;
};

} // namespace bugprone
} // namespace tidy
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SMARTPTRARRAYMISMATCHCHECK_H
4 changes: 4 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Expand Up @@ -99,6 +99,10 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^

- New :doc:`bugprone-shared-ptr-array-mismatch <clang-tidy/checks/bugprone-shared-ptr-array-mismatch>` check.

Finds initializations of C++ shared pointers to non-array type that are initialized with an array.

New check aliases
^^^^^^^^^^^^^^^^^

Expand Down
@@ -0,0 +1,31 @@
.. title:: clang-tidy - bugprone-shared-ptr-array-mismatch

bugprone-shared-ptr-array-mismatch
==================================

Finds initializations of C++ shared pointers to non-array type that are
initialized with an array.

If a shared pointer ``std::shared_ptr<T>`` is initialized with a new-expression
``new T[]`` the memory is not deallocated correctly. The pointer uses plain
``delete`` in this case to deallocate the target memory. Instead a ``delete[]``
call is needed. A ``std::shared_ptr<T[]>`` calls the correct delete operator.

The check offers replacement of ``shared_ptr<T>`` to ``shared_ptr<T[]>`` if it
is used at a single variable declaration (one variable in one statement).

Example:

.. code-block:: c++

std::shared_ptr<Foo> x(new Foo[10]); // -> std::shared_ptr<Foo[]> x(new Foo[10]);
// ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]
std::shared_ptr<Foo> x1(new Foo), x2(new Foo[10]); // no replacement
// ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]

std::shared_ptr<Foo> x3(new Foo[10], [](const Foo *ptr) { delete[] ptr; }); // no warning
struct S {
std::shared_ptr<Foo> x(new Foo[10]); // no replacement in this case
// ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]
};
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Expand Up @@ -84,6 +84,7 @@ Clang-Tidy Checks
`bugprone-posix-return <bugprone-posix-return.html>`_, "Yes"
`bugprone-redundant-branch-condition <bugprone-redundant-branch-condition.html>`_, "Yes"
`bugprone-reserved-identifier <bugprone-reserved-identifier.html>`_, "Yes"
`bugprone-shared-ptr-array-mismatch <bugprone-shared-ptr-array-mismatch.html>`_, "Yes"
`bugprone-signal-handler <bugprone-signal-handler.html>`_,
`bugprone-signed-char-misuse <bugprone-signed-char-misuse.html>`_,
`bugprone-sizeof-container <bugprone-sizeof-container.html>`_,
Expand Down

0 comments on commit c63522e

Please sign in to comment.