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 + UseCopyCheck.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 "UseCopyCheck.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-copy"); CheckFactories.registerCheck("modernize-use-default"); CheckFactories.registerCheck("modernize-use-emplace"); CheckFactories.registerCheck("modernize-use-nullptr"); Index: clang-tidy/modernize/UseCopyCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/UseCopyCheck.h @@ -0,0 +1,44 @@ +//===--- UseCopyCheck.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_COPY_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_COPY_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-copy.html +class UseCopyCheck : public ClangTidyCheck { +public: + UseCopyCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IncludeStyle(utils::IncludeSorter::parseIncludeStyle( + Options.get("IncludeStyle", "llvm"))) {} + + 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; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_COPY_H Index: clang-tidy/modernize/UseCopyCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/UseCopyCheck.cpp @@ -0,0 +1,87 @@ +//===--- UseCopyCheck.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 "UseCopyCheck.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); +} + +void UseCopyCheck::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(hasName("memcpy"))), + argumentCountIs(3))) + .bind("expr"), + this); +} + +void UseCopyCheck::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.reset(new utils::IncludeInserter( + Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle)); + Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks()); + } +} + +void UseCopyCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedExpr = Result.Nodes.getNodeAs("expr"); + const auto Loc = MatchedExpr->getExprLoc(); + + // Don't make replacements in macro + if (Loc.isMacroID()) + return; + + const auto &SM = *Result.SourceManager; + const auto &LangOptions = Result.Context->getLangOpts(); + + StringRef Destination = getText(MatchedExpr->getArg(0), SM, LangOptions); + StringRef Source = getText(MatchedExpr->getArg(1), SM, LangOptions); + StringRef Count = getText(MatchedExpr->getArg(2), SM, LangOptions); + + const std::string Insertion = "std::copy(" + Source.str() + ", (" + + Source.str() + ") + (" + Count.str() + "), " + + Destination.str() + ")"; + + auto Diag = diag(Loc, "Use std::copy instead of memcpy"); + 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,11 @@ Flags slicing of member variables or vtable. +- New `modernize-replace-memcpy + `_ check + + Replaces calls to memcpy with std::copy. + 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-copy modernize-use-default modernize-use-emplace modernize-use-nullptr Index: docs/clang-tidy/checks/modernize-use-copy.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-use-copy.rst @@ -0,0 +1,32 @@ +.. title:: clang-tidy - modernize-use-copy + +modernize-use-copy +================== + +Replaces ``memcpy`` with ``std::copy``. This makes the code cleaner and allows +the compiler to decide on the preferred implementation. + +Example: + +.. code:: c++ + + std::memcpy(dest, source, sizeof dest); + + // transforms to: + + std::copy(source, (source) + (sizeof dest), dest); + +Parenthesis are added devensively to preclude the summation from taking +precedence over operators used in the arguments of ``memcpy``. + +.. code:: c++ + + std::memcpy(baz, b ? foo : bar, 3); + + // transforms to: + + std::copy(b ? foo : bar, (b ? foo : bar) + (3), baz); + + // but without the parenthesis it would have become the equivalent of: + + std::copy(b ? foo : bar, b ? foo : (bar + 3), baz); Index: test/clang-tidy/modernize-use-copy.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-copy.cpp @@ -0,0 +1,38 @@ +// RUN: %check_clang_tidy %s modernize-use-copy %t + +// CHECK-FIXES: #include + +namespace std { +typedef unsigned int size_t; +void *memcpy(void *dest, const void *src, std::size_t count); + +template +OutputIt copy(InputIt first, InputIt last, OutputIt d_first); +} + +void f() { + char foo[] = "foo", bar[3], baz[3]; + std::memcpy(bar, foo, sizeof bar); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use std::copy instead of memcpy + // [modernize-use-copy] + // CHECK-FIXES: std::copy(foo, (foo) + (sizeof bar), bar); + + bool b = false; + std::memcpy(baz, b ? foo : bar, 3); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use std::copy instead of memcpy + // [modernize-use-copy] + // CHECK-FIXES: std::copy(b ? foo : bar, (b ? foo : bar) + (3), baz); + + std::memcpy(bar, foo, sizeof bar); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use std::copy instead of memcpy + // [modernize-use-copy] + // CHECK-FIXES: std::copy(foo, (foo) + (sizeof bar), bar); + + std::copy(foo, foo + sizeof bar, bar); +} + +#define memcpy(dest, src, len) std::memcpy((dest), (src), (len)) +void g() { + char foo[] = "foo", bar[3]; + memcpy(bar, foo, sizeof bar); +}