Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -16,6 +16,7 @@ ShrinkToFitCheck.cpp UseAutoCheck.cpp UseBoolLiteralsCheck.cpp + UseAlgorithmCheck.cpp UseDefaultCheck.cpp UseEmplaceCheck.cpp UseNullptrCheck.cpp Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -22,6 +22,7 @@ #include "ShrinkToFitCheck.h" #include "UseAutoCheck.h" #include "UseBoolLiteralsCheck.h" +#include "UseAlgorithmCheck.h" #include "UseDefaultCheck.h" #include "UseEmplaceCheck.h" #include "UseNullptrCheck.h" @@ -55,6 +56,8 @@ CheckFactories.registerCheck("modernize-use-auto"); CheckFactories.registerCheck( "modernize-use-bool-literals"); + CheckFactories.registerCheck( + "modernize-use-algorithm"); CheckFactories.registerCheck("modernize-use-default"); CheckFactories.registerCheck("modernize-use-emplace"); CheckFactories.registerCheck("modernize-use-nullptr"); Index: clang-tidy/modernize/UseAlgorithmCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/UseAlgorithmCheck.h @@ -0,0 +1,42 @@ +//===--- UseAlgorithmCheck.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_MODERNIZE_USE_ALGORITHM_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_ALGORITHM_H + +#include "../ClangTidy.h" +#include "../utils/IncludeInserter.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// Replaces memcpy with std::copy +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-algorithm.html +class UseAlgorithmCheck : public ClangTidyCheck { +public: + UseAlgorithmCheck(StringRef Name, ClangTidyContext *Context); + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void registerPPCallbacks(CompilerInstance &Compiler) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + std::unique_ptr Inserter; + const utils::IncludeSorter::IncludeStyle IncludeStyle; + llvm::StringMap Replacements; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_ALGORITHM_H Index: clang-tidy/modernize/UseAlgorithmCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/UseAlgorithmCheck.cpp @@ -0,0 +1,148 @@ +//===--- UseAlgorithmCheck.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 "UseAlgorithmCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" + +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +static StringRef getText(const Expr *Expression, const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getSourceText( + CharSourceRange::getTokenRange(Expression->getSourceRange()), SM, + LangOpts); +} + +static std::string getReplacement(const StringRef Function, + const StringRef Arg0, const StringRef Arg1, + const StringRef Arg2) { + return Function.str() + "(" + Arg0.str() + ", " + Arg1.str() + ", " + + Arg2.str() + ")"; +} + +static const CallExpr *getCallExpr(const MatchFinder::MatchResult &Result) { + if (const auto *MemcpyExpr = Result.Nodes.getNodeAs("memcpy")) { + return MemcpyExpr; + } else if (const auto *MemmoveExpr = + Result.Nodes.getNodeAs("memmove")) { + return MemmoveExpr; + } else if (const auto *MemsetExpr = + Result.Nodes.getNodeAs("memset")) { + return MemsetExpr; + } + return nullptr; +} + +UseAlgorithmCheck::UseAlgorithmCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IncludeStyle(utils::IncludeSorter::parseIncludeStyle( + Options.get("IncludeStyle", "llvm"))) { + + for (const auto &KeyValue : + std::vector>( + {{"memcpy", "std::copy"}, + {"memmove", "std::move"}, + {"memset", "std::fill"}})) { + Replacements.insert(KeyValue); + } +} + +void UseAlgorithmCheck::registerMatchers(MatchFinder *Finder) { + // Only register the matchers for C++ + if (!getLangOpts().CPlusPlus) + return; + + // Match calls to memcpy with 3 arguments. + Finder->addMatcher( + callExpr(allOf(callee(functionDecl(matchesName("::memcpy"))), + argumentCountIs(3))) + .bind("memcpy"), + this); + + // Match calls to memmove with 3 arguments. + Finder->addMatcher( + callExpr(allOf(callee(functionDecl(matchesName("::memmove"))), + argumentCountIs(3))) + .bind("memmove"), + this); + + // Match calls to memset with 3 arguments. + Finder->addMatcher( + callExpr(allOf(callee(functionDecl(matchesName("::memset"))), + argumentCountIs(3))) + .bind("memset"), + this); +} + +void UseAlgorithmCheck::registerPPCallbacks(CompilerInstance &Compiler) { + // Only register the preprocessor callbacks for C++; the functionality + // currently does not provide any benefit to other languages, despite being + // benign. + if (getLangOpts().CPlusPlus) { + Inserter = llvm::make_unique( + Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle); + Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks()); + } +} + +void UseAlgorithmCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedExpr = getCallExpr(Result); + const auto MatchedName = MatchedExpr->getDirectCallee()->getNameAsString(); + const auto ReplacedName = Replacements[MatchedName]; + + const auto Loc = MatchedExpr->getExprLoc(); + auto Diag = diag(Loc, "Use " + ReplacedName + " instead of " + MatchedName); + + // Don't make replacements in macro. + if (Loc.isMacroID()) + return; + + const auto &SM = *Result.SourceManager; + const auto &LangOptions = Result.Context->getLangOpts(); + + StringRef Arg0Text = getText(MatchedExpr->getArg(0), SM, LangOptions); + StringRef Arg1Text = getText(MatchedExpr->getArg(1), SM, LangOptions); + StringRef Arg2Text = getText(MatchedExpr->getArg(2), SM, LangOptions); + + std::string Insertion; + if (MatchedName == "memset") { + // Rearrangement of arguments for memset: + // (dest, ch, count) becomes (dest, dest + count, ch). + Insertion = getReplacement(ReplacedName, Arg0Text, + (Arg0Text + " + " + Arg2Text).str(), Arg1Text); + } else { + // Rearrangement of arguments for memcpy and memmove: + // (dest, src, count) becomes (src, src + count, dest). + Insertion = getReplacement(ReplacedName, Arg1Text, + (Arg1Text + " + " + Arg2Text).str(), Arg0Text); + } + + Diag << FixItHint::CreateReplacement(MatchedExpr->getSourceRange(), + Insertion); + + if (auto IncludeFixit = Inserter->CreateIncludeInsertion( + Result.SourceManager->getFileID(Loc), "algorithm", + /*IsAngled=*/true)) { + Diag << *IncludeFixit; + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -64,6 +64,13 @@ Flags slicing of member variables or vtable. +- New `modernize-use-algorithm + `_ check + + Replaces calls to ``memcpy``, ``memmove`` and ``memset`` with their + respective algorithm counterparts ``std::copy``, ``std::move`` and + ``std::fill``. + Improvements to include-fixer ----------------------------- Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -29,6 +29,7 @@ cppcoreguidelines-pro-type-static-cast-downcast cppcoreguidelines-pro-type-union-access cppcoreguidelines-pro-type-vararg + cppcoreguidelines-slicing google-build-explicit-make-pair google-build-namespaces google-build-using-namespace @@ -103,6 +104,7 @@ modernize-shrink-to-fit modernize-use-auto modernize-use-bool-literals + modernize-use-algorithm modernize-use-default modernize-use-emplace modernize-use-nullptr Index: docs/clang-tidy/checks/modernize-use-algorithm.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-use-algorithm.rst @@ -0,0 +1,49 @@ +.. title:: clang-tidy - modernize-use-algorithm + +modernize-use-algorithm +======================= + +Replaces calls to ``memcpy``, ``memmove`` and ``memset`` with ``std::copy``, +``std::move`` and ``std::fill`` respectively. The advantages of using these +algorithm functions is that they are at least as efficient, more general and +type-aware. + +Furthermore, by using the algorithms the types remain intact as opposed to +being discarded by the C-style functions. This allows the implementation to +make use use of type information to further optimize. One example of such +optimization is taking advantage of 64-bit alignment when copying an array of +``std::uint64_t``. + +memcpy +------ + +.. code:: c++ + + std::memcpy(dest, source, sizeof dest); + + // transforms to: + + std::copy(source, source + sizeof dest, dest); + +memmove +------- + +.. code:: c++ + + std::memmove(dest, source, sizeof dest); + + // transforms to: + + std::move(source, source + sizeof dest, dest); + +memset +------ + +.. code:: c++ + + std::memset(dest, ch, count); + + // transforms to: + + std::fill(dest, dest + count, ch) + Index: test/clang-tidy/modernize-use-algorithm.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-algorithm.cpp @@ -0,0 +1,53 @@ +// RUN: %check_clang_tidy %s modernize-use-algorithm %t + +// CHECK-FIXES: #include + +namespace std { +typedef unsigned int size_t; +void *memcpy(void *dest, const void *src, std::size_t count); +void *memset(void* dest, int ch, std::size_t count); +void *memmove(void* dest, const void* src, std::size_t count); + +template +OutputIt copy(InputIt first, InputIt last, OutputIt d_first); + +template +OutputIt move(InputIt first, InputIt last, OutputIt d_first); + +template +void fill(ForwardIt first, ForwardIt last, const T& value); +} + +void a() { + char foo[] = "foo", bar[3]; + std::memcpy(bar, foo, sizeof bar); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use std::copy instead of memcpy [modernize-use-algorithm] + // CHECK-FIXES: std::copy(foo, foo + sizeof bar, bar); + + std::copy(foo, foo + sizeof bar, bar); +} + +void b() { + char foo[] = "foobar"; + std::memmove(foo + 1, foo , 3); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use std::move instead of memmove [modernize-use-algorithm] + // CHECK-FIXES: std::move(foo, foo + 3, foo + 1); + + std::move(foo, foo + 2, foo + 2); +} + +void c() { + char foo[] = "foobar"; + std::memset(foo, 1, 3); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use std::fill instead of memset [modernize-use-algorithm] + // CHECK-FIXES: std::fill(foo, foo + 3, 1); + + std::move(foo, foo + 2, 'a'); +} + +#define MEMCPY(dest, src) std::memcpy((dest), (src), sizeof (dest)) +void d() { + char foo[] = "foo", bar[3]; + MEMCPY(bar, foo); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use std::copy instead of memcpy [modernize-use-algorithm] +}