Index: clang-tidy/cppcoreguidelines/NoMallocCheck.h =================================================================== --- clang-tidy/cppcoreguidelines/NoMallocCheck.h +++ clang-tidy/cppcoreguidelines/NoMallocCheck.h @@ -27,14 +27,35 @@ /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html class NoMallocCheck : public ClangTidyCheck { public: + /// Construct Checker and read in configuration for function names. NoMallocCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + : ClangTidyCheck(Name, Context), + AllocList(Options.get("Allocations", "::malloc;::calloc")), + ReallocList(Options.get("Reallocations", "::realloc")), + DeallocList(Options.get("Deallocations", "::free")) {} + + /// Make configuration of checker discoverable. + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; /// 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: + /// Returns a matcher for all configure allocation functions. + ast_matchers::internal::Matcher allocationFunctions() const; + /// Same as allocationFunctions but for reallocation functions. + ast_matchers::internal::Matcher reallocationFunctions() const; + /// Same as allocationFunctions but for deallocation/free functions. + ast_matchers::internal::Matcher deallocationFunctions() const; + + /// Each variable is a list of comma-seperated function names (with namespace) + /// to memory management functions. + const std::string AllocList; + const std::string ReallocList; + const std::string DeallocList; }; } // namespace cppcoreguidelines Index: clang-tidy/cppcoreguidelines/NoMallocCheck.cpp =================================================================== --- clang-tidy/cppcoreguidelines/NoMallocCheck.cpp +++ clang-tidy/cppcoreguidelines/NoMallocCheck.cpp @@ -8,17 +8,27 @@ //===----------------------------------------------------------------------===// #include "NoMallocCheck.h" +#include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" -#include +#include #include +#include using namespace clang::ast_matchers; +using namespace clang::ast_matchers::internal; namespace clang { namespace tidy { namespace cppcoreguidelines { +void NoMallocCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "Allocations", AllocList); + Options.store(Opts, "Reallocations", ReallocList); + Options.store(Opts, "Deallocations", DeallocList); +} + void NoMallocCheck::registerMatchers(MatchFinder *Finder) { // C-style memory management is only problematic in C++. if (!getLangOpts().CPlusPlus) @@ -26,25 +36,25 @@ // Registering malloc, will suggest RAII. Finder->addMatcher( - callExpr(callee(functionDecl(hasAnyName("::malloc", "::calloc")))) - .bind("aquisition"), + callExpr(callee(functionDecl(allocationFunctions()))).bind("allocation"), this); // Registering realloc calls, suggest std::vector or std::string. Finder->addMatcher( - callExpr(callee(functionDecl(hasName("::realloc")))).bind("realloc"), + callExpr(callee(functionDecl(reallocationFunctions()))).bind("realloc"), this); // Registering free calls, will suggest RAII instead. Finder->addMatcher( - callExpr(callee(functionDecl(hasName("::free")))).bind("free"), this); + callExpr(callee(functionDecl(deallocationFunctions()))).bind("free"), + this); } void NoMallocCheck::check(const MatchFinder::MatchResult &Result) { const CallExpr *Call = nullptr; StringRef Recommendation; - if ((Call = Result.Nodes.getNodeAs("aquisition"))) + if ((Call = Result.Nodes.getNodeAs("allocation"))) Recommendation = "consider a container or a smart pointer"; else if ((Call = Result.Nodes.getNodeAs("realloc"))) Recommendation = "consider std::vector or std::string"; @@ -57,6 +67,24 @@ << Recommendation << SourceRange(Call->getLocStart(), Call->getLocEnd()); } +Matcher NoMallocCheck::allocationFunctions() const { + auto NameList = utils::options::parseStringList(AllocList); + assert(NameList.size() > 0 && "no allocation NameList"); + return hasAnyName(std::vector(NameList.begin(), NameList.end())); +} + +Matcher NoMallocCheck::reallocationFunctions() const { + auto NameList = utils::options::parseStringList(ReallocList); + assert(NameList.size() > 0 && "no reallocation NameList"); + return hasAnyName(std::vector(NameList.begin(), NameList.end())); +} + +Matcher NoMallocCheck::deallocationFunctions() const { + auto NameList = utils::options::parseStringList(DeallocList); + assert(NameList.size() > 0 && "no deallocation NameList"); + return hasAnyName(std::vector(NameList.begin(), NameList.end())); +} + } // namespace cppcoreguidelines } // namespace tidy } // namespace clang Index: docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst =================================================================== --- docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst +++ docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst @@ -6,8 +6,9 @@ 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 -`. +Furthermore, it can be configured to check against other/more functions that are used +for memory management (e.g. ``posix_memalign()``). +See `C++ Core Guidelines `. There is no attempt made to provide fixit hints, since manual resource management isn't easily transformed automatically into RAII. @@ -25,3 +26,22 @@ // Rather use a smartpointer or stack variable. struct some_struct* s = (struct some_struct*) malloc(sizeof(struct some_struct)); +Options +------- + +Each option consists of a semicolon separated list of functions that are checked for each +memory management usecase. The full namespace must be given to function correctly and +each options needs at least one function. + +.. option:: Allocations + + List of allocation functions, defaults to ``::malloc;::calloc``. + +.. option:: Deallocations + + List of deallocation functions, defaults to ``::free``. + +.. option:: Reallocations + + List of reallocation functions, defaults to ``::realloc``. + Index: test/clang-tidy/cppcoreguidelines-no-malloc-custom.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-no-malloc-custom.cpp @@ -0,0 +1,59 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: cppcoreguidelines-no-malloc.Allocations, value: "::malloc;::align_malloc;::calloc"},\ +// RUN: {key: cppcoreguidelines-no-malloc.Reallocations, value: "::realloc;::align_realloc"},\ +// RUN: {key: cppcoreguidelines-no-malloc.Deallocations, value: "::free;::align_free"}]}' \ +// RUN: -- + +using size_t = __SIZE_TYPE__; + +void *malloc(size_t size); +void *align_malloc(size_t size, unsigned short aligmnent); +void *calloc(size_t num, size_t size); +void *realloc(void *ptr, size_t size); +void *align_realloc(void *ptr, size_t size, unsigned short alignment); +void free(void *ptr); +void *align_free(void *ptr); + +void malloced_array() { + int *array0 = (int *)malloc(sizeof(int) * 20); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc] + + int *zeroed = (int *)calloc(20, sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc] + + int *aligned = (int *)align_malloc(20 * sizeof(int), 16); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc] + + // 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; consider std::vector or std::string [cppcoreguidelines-no-malloc] + + char *align_realloced = (char *)align_realloc(aligned, 50 * sizeof(int), 16); + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: do not manage memory manually; consider std::vector or std::string [cppcoreguidelines-no-malloc] + + // freeing memory the bad way + free(realloced); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc] + + align_free(align_realloced); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc] + + // 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; use RAII [cppcoreguidelines-no-malloc] + // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: do not manage memory manually; consider a container or a smart pointer [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 + malloced_array(); // OK(2) + + // completly unrelated function call to malloc + newed_array(); // OK(3) +}