Index: clang-tidy/cppcoreguidelines/CMakeLists.txt =================================================================== --- clang-tidy/cppcoreguidelines/CMakeLists.txt +++ clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule CppCoreGuidelinesTidyModule.cpp InterfacesGlobalInitCheck.cpp + NoMallocCheck.cpp ProBoundsArrayToPointerDecayCheck.cpp ProBoundsConstantArrayIndexCheck.cpp ProBoundsPointerArithmeticCheck.cpp Index: clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp =================================================================== --- clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "../misc/UnconventionalAssignOperatorCheck.h" #include "InterfacesGlobalInitCheck.h" +#include "NoMallocCheck.h" #include "ProBoundsArrayToPointerDecayCheck.h" #include "ProBoundsConstantArrayIndexCheck.h" #include "ProBoundsPointerArithmeticCheck.h" @@ -35,6 +36,7 @@ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "cppcoreguidelines-interfaces-global-init"); + CheckFactories.registerCheck("cppcoreguidelines-no-malloc"); CheckFactories.registerCheck( "cppcoreguidelines-pro-bounds-array-to-pointer-decay"); CheckFactories.registerCheck( Index: clang-tidy/cppcoreguidelines/NoMallocCheck.h =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/NoMallocCheck.h @@ -0,0 +1,55 @@ +//===--- NoMallocCheck.h - clang-tidy----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H + +#include "../ClangTidy.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// This checker is concerned with C-style memory management and suggest modern +/// alternatives to it. +/// The check is only enabled in C++. For analyzing malloc calls see Clang +/// Static Analyzer - unix.Malloc. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html +class NoMallocCheck : public ClangTidyCheck { +public: + NoMallocCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + /// Registering for malloc, calloc, realloc and free calls. + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + + /// Checks matched function calls and gives suggestion to modernize the code. + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + /// Suggests the use of a Container or Smartpointer instead of malloc or + /// calloc + void handleAquisition(const CallExpr *AquisitionCall); + + /// Suggests the use of std::vector or std::string when resizing is wanted. + void handleRealloc(const CallExpr *ReallocCall); + + /// Suggest RAII instead of manual deletion of allocated memory. + void handleFree(const CallExpr *FreeCall); +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H Index: clang-tidy/cppcoreguidelines/NoMallocCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/NoMallocCheck.cpp @@ -0,0 +1,82 @@ +//===--- NoMallocCheck.cpp - clang-tidy------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NoMallocCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +void NoMallocCheck::registerMatchers(MatchFinder *Finder) { + // C style memory management is only problematic in C++. + if (!getLangOpts().CPlusPlus) + return; + + // Registering malloc, will suggest RAII. + Finder->addMatcher( + callExpr(callee(functionDecl(hasAnyName("::malloc", "::calloc")))) + .bind("aquisition"), + this); + + // Registering realloc calls, suggest std::vector or std::string. + Finder->addMatcher( + callExpr(callee(functionDecl(hasName("::realloc")))).bind("realloc"), + this); + + // Registering free calls, will suggest RAII instead. + Finder->addMatcher( + callExpr(callee(functionDecl(hasName("::free")))).bind("free"), this); +} + +void NoMallocCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *MallocMatch = Result.Nodes.getNodeAs("aquisition")) + handleAquisition(MallocMatch); + + if (const auto *ReallocMatch = Result.Nodes.getNodeAs("realloc")) + handleRealloc(ReallocMatch); + + if (const auto *FreeMatch = Result.Nodes.getNodeAs("free")) + handleFree(FreeMatch); +} + +void NoMallocCheck::handleAquisition(const CallExpr *AquisitionCall) { + diag(AquisitionCall->getLocStart(), "Do not allocate memory manually. [R.alloc]") + << SourceRange{AquisitionCall->getLocStart(), + AquisitionCall->getLocEnd()}; + + diag(AquisitionCall->getLocStart(), + "Consider using a Container or Smartpointer.", DiagnosticIDs::Note); +} + +void NoMallocCheck::handleRealloc(const CallExpr *ReallocCall) { + diag(ReallocCall->getLocStart(), "Do not manage memory manually. [R.alloc]") + << SourceRange{ReallocCall->getLocStart(), ReallocCall->getLocEnd()}; + + diag(ReallocCall->getLocStart(), + "std::vector or std::string might be a good replacement. [SL.con]", + DiagnosticIDs::Note); +} + +void NoMallocCheck::handleFree(const CallExpr *FreeCall) { + diag(FreeCall->getLocStart(), "Do not manage memory manually. [R.alloc]") + << SourceRange{FreeCall->getLocStart(), FreeCall->getLocEnd()}; + + diag(FreeCall->getLocStart(), + "Use Objects that allocate and free their memory. [RAII]", DiagnosticIDs::Note); +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -81,6 +81,10 @@ Warns if an object is used after it has been moved, without an intervening reinitialization. +- New `cppcoreguidelines-no-malloc + `_ check + warns if C-style memory management is used and suggests the use of RAII. + - `modernize-make-unique `_ and `modernize-make-shared Index: docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst @@ -0,0 +1,26 @@ +.. title:: clang-tidy - cppcoreguidelines-no-malloc + +cppcoreguidelines-no-malloc +=================== + +This check handles C-Style memory management using ``malloc()``, ``realloc()``, +``calloc()`` and ``free()``. It warns about its use and tries to suggest the use +of an appropriate RAII object. +See `C++ Core Guidelines +` + +There is no attempt made to provide fixit hints, since manual resource management isn't +easily transformed automatically into RAII. + +.. code-block:: c++ + + // Warns each of the following lines. + char* some_string = (char*) malloc(sizeof(char) * 20); // use std::string instead + char* some_string = (char*) realloc(sizeof(char) * 30);// same + free(some_string); + + int* int_array = (int*) calloc(30, sizeof(int)); // use std::vector + + // rather use a smartpointer or stack variable + struct some_struct* = (struct some_struct*) malloc(sizeof(struct some_struct)); + Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -22,6 +22,7 @@ cert-msc50-cpp cert-oop11-cpp (redirects to misc-move-constructor-init) cppcoreguidelines-interfaces-global-init + cppcoreguidelines-no-malloc cppcoreguidelines-pro-bounds-array-to-pointer-decay cppcoreguidelines-pro-bounds-constant-array-index cppcoreguidelines-pro-bounds-pointer-arithmetic Index: test/clang-tidy/cppcoreguidelines-no-malloc.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-no-malloc.cpp @@ -0,0 +1,48 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t + +using size_t = unsigned long; + +void *malloc(size_t size); +void *calloc(size_t num, size_t size); +void *realloc(void *ptr, size_t size); +void free(void *ptr); + +void malloced_array() { + int *array0 = (int *)malloc(sizeof(int) * 20); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: Do not allocate memory manually. [R.alloc] [cppcoreguidelines-no-malloc] + // CHECK-MESSAGES: :[[@LINE-2]]:24: note: Consider using a Container or Smartpointer. + + int *zeroed = (int *)calloc(20, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: Do not allocate memory manually. [R.alloc] [cppcoreguidelines-no-malloc] + // CHECK-MESSAGES: :[[@LINE-2]]:24: note: Consider using a Container or Smartpointer. + + // reallocation memory, std::vector shall be used + char *realloced = (char *)realloc(array0, 50 * sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: Do not manage memory manually. [R.alloc] [cppcoreguidelines-no-malloc] + // CHECK-MESSAGES: :[[@LINE-2]]:29: note: std::vector or std::string might be a good replacement. [SL.con] + + // freeing memory the bad way + free(realloced); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not manage memory manually. [R.alloc] [cppcoreguidelines-no-malloc] + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: Use Objects that allocate and free their memory. [RAII] + + // check if a call to malloc as function argument is found as well + free(malloc(20)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Do not manage memory manually. [R.alloc] [cppcoreguidelines-no-malloc] + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: Use Objects that allocate and free their memory. + // CHECK-MESSAGES: :[[@LINE-3]]:8: warning: Do not allocate memory manually. [R.alloc] [cppcoreguidelines-no-malloc] +} + +/// newing an array is still not good, but not relevant to this checker +void newed_array() { + int *new_array = new int[10]; // OK(1) +} + +void arbitrary_call() { + // we dont want every function to raise the warning + // even if malloc is in the name? this could be unwanted + malloced_array(); // OK(2) + + // completly unrelated function call to malloc + newed_array(); // OK(3) +}