diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index c919d49b42873..83ebd2f12775e 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -23,6 +23,7 @@ add_clang_library(clangTidyModernizeModule STATIC RedundantVoidArgCheck.cpp ReplaceAutoPtrCheck.cpp ReplaceDisallowCopyAndAssignMacroCheck.cpp + ReplaceMemcpyWithStdCopy.cpp ReplaceRandomShuffleCheck.cpp ReturnBracedInitListCheck.cpp ShrinkToFitCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 1860759332063..b36248ce1411d 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -24,6 +24,7 @@ #include "RedundantVoidArgCheck.h" #include "ReplaceAutoPtrCheck.h" #include "ReplaceDisallowCopyAndAssignMacroCheck.h" +#include "ReplaceMemcpyWithStdCopy.h" #include "ReplaceRandomShuffleCheck.h" #include "ReturnBracedInitListCheck.h" #include "ShrinkToFitCheck.h" @@ -91,6 +92,8 @@ class ModernizeModule : public ClangTidyModule { "modernize-replace-auto-ptr"); CheckFactories.registerCheck( "modernize-replace-disallow-copy-and-assign-macro"); + CheckFactories.registerCheck( + "modernize-replace-memcpy-with-std-copy"); CheckFactories.registerCheck( "modernize-replace-random-shuffle"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp new file mode 100644 index 0000000000000..eb7b89e89f5a3 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp @@ -0,0 +1,313 @@ +//===--- ReplaceMemcpyWithStdCopy.cpp - 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 +// +//===----------------------------------------------------------------------===// + +#include "ReplaceMemcpyWithStdCopy.h" +#include "../utils/Matchers.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Tooling/FixIt.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include +#include +#include +#include + +using namespace clang; +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { +static constexpr llvm::StringLiteral MemcpyRef = "::std::memcpy"; + +namespace { +// Helper Matcher which applies the given QualType Matcher either directly or by +// resolving a pointer type to its pointee. Used to match v.push_back() as well +// as p->push_back(). +auto hasTypeOrPointeeType( + const ast_matchers::internal::Matcher &TypeMatcher) { + return anyOf(hasType(TypeMatcher), + hasType(pointerType(pointee(TypeMatcher)))); +} + +constexpr llvm::StringLiteral StdCopyNHeader = ""; +} // namespace + +ReplaceMemcpyWithStdCopy::ReplaceMemcpyWithStdCopy(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + Inserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()) {} + +void ReplaceMemcpyWithStdCopy::registerMatchers(MatchFinder *Finder) { + const auto ByteLikeTypeWithId = [](const char *ID) { + return anything(); + // return hasCanonicalType(matchers::isSimpleChar()); + }; + + const auto IsByteSequence = anything(); + // hasTypeOrPointeeType( + // hasCanonicalType(hasDeclaration(cxxRecordDecl(hasAnyName({ + // "::std::vector", + // "::std::span", + // "::std::deque", + // "::std::array", + // "::std::string", + // "::std::string_view", + // }))))); + // __jm__ for template classes need to add a check whether T is ByteLike + // __jm__ these matchers unused because memcpy for non-byte sequences should + // be flagged without FixIt + + const auto IsDestContainerData = cxxMemberCallExpr( + callee(cxxMethodDecl( + hasName("data"), + returns(pointerType(pointee(ByteLikeTypeWithId("data_return_type")))), + unless(isConst()))), + argumentCountIs(0), on(expr(IsByteSequence))); + + const auto IsSourceContainerData = cxxMemberCallExpr( + callee(cxxMethodDecl( + hasName("data"), + returns(pointerType(pointee(ByteLikeTypeWithId("data_return_type")))) + //,isConst() __jm__ doesn't have to be const pre-implicit cast, but + // that would require switching traversal type + )), + argumentCountIs(0), + on(expr(IsByteSequence))); // __jm__ how to express that the caller needs + // to be const qualified / a const lvalue? + + auto IsDestCArrayOrContainerData = + anyOf(IsDestContainerData.bind("dest_data"), + hasType(hasCanonicalType(arrayType().bind("dest_array")))); + + auto IsSourceCArrayOrContainerData = + anyOf(IsSourceContainerData, hasType(hasCanonicalType(arrayType()))); + // all of the pointer args to memcpy must be any of: + // 1. .data() call to record which + // can be passed to std::begin or has .begin method and + // can be passed to std::end or has .end method and + // can be passed to std::size or has .size method + // 2. static c-array + const auto ReturnValueUsed = + optionally(hasParent(compoundStmt().bind("return_value_discarded"))); + + const auto MemcpyDecl = + functionDecl(hasAnyName("::std::memcpy", "::memcpy") + // , argumentCountIs(3) __jm__ doesn't work for + // functionDecl, possibly only for callExpr? + ); + // __jm__ also match on arg types + const auto Expression = + callExpr(callee(MemcpyDecl), ReturnValueUsed, + hasArgument(0, expr(IsDestCArrayOrContainerData).bind("dest")), + hasArgument(1, IsSourceCArrayOrContainerData)); + Finder->addMatcher(Expression.bind(MemcpyRef), this); +} + +void ReplaceMemcpyWithStdCopy::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + Inserter.registerPreprocessor(PP); +} + +void ReplaceMemcpyWithStdCopy::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeStyle", Inserter.getStyle()); +} + +// Determine if the result of an expression is "stored" in some way. +// It is true if the value is stored into a variable or used as initialization +// or passed to a function or constructor. +// For this use case compound assignments are not counted as a "store" (the 'E' +// expression should have pointer type). +static bool isExprValueStored(const Expr *E, ASTContext &C) { + E = E->IgnoreParenCasts(); + // Get first non-paren, non-cast parent. + ParentMapContext &PMap = C.getParentMapContext(); + DynTypedNodeList P = PMap.getParents(*E); + if (P.size() != 1) + return false; + const Expr *ParentE = nullptr; + while ((ParentE = P[0].get()) && ParentE->IgnoreParenCasts() == E) { + P = PMap.getParents(P[0]); + if (P.size() != 1) + return false; + } + + if (const auto *ParentVarD = P[0].get()) + return ParentVarD->getInit()->IgnoreParenCasts() == E; + + if (!ParentE) + return false; + + if (const auto *BinOp = dyn_cast(ParentE)) + return BinOp->getOpcode() == BO_Assign && + BinOp->getRHS()->IgnoreParenCasts() == E; + + return isa(ParentE); +} +#include "llvm/Support/Debug.h" +#define DEBUG_TYPE "clang-tidy" + +bool ReplaceMemcpyWithStdCopy::checkIsByteSequence( + const MatchFinder::MatchResult &Result, std::string_view Prefix) { + + const auto &ArgNode = *Result.Nodes.getNodeAs("dest"); + ArgNode.dumpColor(); + + auto PointerType_ = ArgNode.getType()->getAs(); + if (PointerType_ == nullptr) + return false; + + auto PointeeType = PointerType_->getPointeeType(); + // if + // (StructFieldTy->isIncompleteType()) + // return false; + auto PointeeWidth = + Result.Context->getTypeInfo(PointeeType.getTypePtr()).Width; + + bool IsArgPtrToBytelike = PointeeWidth == Result.Context->getCharWidth(); + + LLVM_DEBUG(llvm::dbgs() << "__jm__ dbgs\n"); + // ArgNode.dumpPretty(*Result.Context); + ArgNode.dumpColor(); + if (ArgNode.IgnoreImplicit()->getType()->isArrayType()) { + LLVM_DEBUG(llvm::dbgs() + << name() << "__jm__ array \n typewidth:" << PointeeWidth + << "\n charwidth:" << Result.Context->getCharWidth()); + + return IsArgPtrToBytelike; + } + + // ArgNode is a result of either std::data or .data() + if (const auto *MC = + dyn_cast_or_null(ArgNode.IgnoreCasts()); + MC != nullptr) { + // return false; + LLVM_DEBUG(llvm::dbgs() << "__jm__ member call"); + // diag(ArgNode.getExprLoc(), "%0 isnull") + // << (MC->getMethodDecl() == nullptr); + } else { + LLVM_DEBUG(llvm::dbgs() << "__jm__ otherwise"); + } + return false; + // else if (auto *FC = P->get(); FC != nullptr) { + // diag(ArgNode.getExprLoc(), "%0 _2 name is") + // << + // FC->getCalleeDecl()->getCanonicalDecl()->getAsFunction()->getName(); + // } +} + +void ReplaceMemcpyWithStdCopy::check(const MatchFinder::MatchResult &Result) { + const auto *MemcpyNode = Result.Nodes.getNodeAs(MemcpyRef); + assert(MemcpyNode != nullptr && "Matched node cannot be null"); + + auto Diag = diag(MemcpyNode->getExprLoc(), "prefer std::copy_n to memcpy"); + + // don't issue a fixit if the result of the call is used + // if (bool IsMemcpyReturnValueUsed = + // Result.Nodes.getNodeAs("return_value_discarded") == nullptr; + // IsMemcpyReturnValueUsed) + // return; + + auto *DestArg = MemcpyNode->getArg(0); + auto *SrcArg = MemcpyNode->getArg(1); + auto *SizeArg = MemcpyNode->getArg(2); + + assert(DestArg != nullptr and SrcArg != nullptr and SizeArg != nullptr and + "Call node arguments cannot be null"); + + if (not checkIsByteSequence(Result, "dest") + // or + // not checkIsByteSequence(Result, *SrcArg, Diag) + ) + diag(MemcpyNode->getExprLoc(), "not a byte sequence"); + + // // __jm__ strip .data() / std::data calls + + // // const CharSourceRange FunctionNameSourceRange = + // // CharSourceRange::getCharRange( + // // MemcpyNode->getBeginLoc(), MemcpyNode->getArg(0)->getBeginLoc()); + // Diag << FixItHint::CreateReplacement( + // MemcpyNode->getCallee()->getSourceRange(), "std::copy_n"); + + { + // using namespace std::literals; + // Diag << FixItHint::CreateReplacement( + // DestArg->getSourceRange(), + // ("std::begin(" + + // tooling::fixit::getText(SrcArg->getSourceRange(), *Result.Context) + + // ")") + // .str()); + // Diag << FixItHint::CreateReplacement( + // SrcArg->getSourceRange(), + // tooling::fixit::getText(SizeArg->getSourceRange(), *Result.Context)); + // Diag << FixItHint::CreateReplacement( + // SizeArg->getSourceRange(), + // ("std::begin(" + + // tooling::fixit::getText(DestArg->getSourceRange(), *Result.Context) + // + + // ")") + // .str()); + } + // dest source size -> source size dest + // renameFunction(Diag, MemcpyNode); + // reorderArgs(Diag, MemcpyNode); + // insertHeader(Diag, MemcpyNode, Result.SourceManager); + + Diag << Inserter.createIncludeInsertion( + Result.SourceManager->getFileID(MemcpyNode->getBeginLoc()), + StdCopyNHeader); +} + +// void ReplaceMemcpyWithStdCopy::handleMemcpy(const CallExpr &Node) { + +// // FixIt +// const CharSourceRange FunctionNameSourceRange = +// CharSourceRange::getCharRange( +// Node.getBeginLoc(), Node.getArg(0)->getBeginLoc()); + +// Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, +// "std::copy_n("); +// } + +void ReplaceMemcpyWithStdCopy::renameFunction(DiagnosticBuilder &Diag, + const CallExpr *MemcpyNode) {} + +void ReplaceMemcpyWithStdCopy::reorderArgs(DiagnosticBuilder &Diag, + const CallExpr *MemcpyNode) { + std::array Arg; + + LangOptions LangOpts; + LangOpts.CPlusPlus = true; + PrintingPolicy Policy(LangOpts); + + // Retrieve all the arguments + for (uint8_t I = 0; I < Arg.size(); I++) { + llvm::raw_string_ostream S(Arg[I]); + MemcpyNode->getArg(I)->printPretty(S, nullptr, Policy); + } + + // Create lambda that return SourceRange of an argument + auto GetSourceRange = [MemcpyNode](uint8_t ArgCount) -> SourceRange { + return SourceRange(MemcpyNode->getArg(ArgCount)->getBeginLoc(), + MemcpyNode->getArg(ArgCount)->getEndLoc()); + }; + + // Reorder the arguments + Diag << FixItHint::CreateReplacement(GetSourceRange(0), Arg[1]); + + Arg[2] = Arg[1] + " + ((" + Arg[2] + ") / sizeof(*(" + Arg[1] + ")))"; + Diag << FixItHint::CreateReplacement(GetSourceRange(1), Arg[2]); + + Diag << FixItHint::CreateReplacement(GetSourceRange(2), Arg[0]); +} +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h new file mode 100644 index 0000000000000..0f847a39f474d --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h @@ -0,0 +1,54 @@ +//===--- ReplaceMemcpyWithStdCopy.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_MODERNIZE_REPLACE_MEMCPY_WITH_STDCOPY_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_MEMCPY_WITH_STDCOPY_H + +#include "../ClangTidyCheck.h" +#include "../utils/IncludeInserter.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/Basic/LangOptions.h" + +namespace clang::tidy::modernize { + +// Replace the C memcpy function with std::copy +class ReplaceMemcpyWithStdCopy : public ClangTidyCheck { +public: + ReplaceMemcpyWithStdCopy(StringRef Name, ClangTidyContext *Context); + ~ReplaceMemcpyWithStdCopy() override = default; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void storeOptions(ClangTidyOptions::OptionMap &Options) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus11; + } + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void handleMemcpy(); + void handleMemset(); + + void renameFunction(DiagnosticBuilder &Diag, const CallExpr *MemcpyNode); + void reorderArgs(DiagnosticBuilder &Diag, const CallExpr *MemcpyNode); + void insertHeader(DiagnosticBuilder &Diag, const CallExpr *MemcpyNode, + SourceManager *const SM); + bool checkIsByteSequence(const ast_matchers::MatchFinder::MatchResult &Result, + std::string_view Prefix); + +private: + utils::IncludeInserter Inserter; +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_MEMCPY_WITH_STDCOPY_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index e8148e06b6af2..cd256bb8074f8 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -217,6 +217,11 @@ Changes in existing checks member function calls too and to only expand macros starting with ``PRI`` and ``__PRI`` from ```` in the format string. +- New :doc:`modernize-replace-memcpy-with-std-copy + ` check. + + Replaces all occurrences of the C ``memcpy`` function by ``std::copy``. + - Improved :doc:`modernize-use-std-print ` check to support replacing member function calls too and to only expand macros starting with ``PRI`` diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 0082234f5ed31..e923f43dd44a6 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -286,6 +286,7 @@ Clang-Tidy Checks :doc:`modernize-raw-string-literal `, "Yes" :doc:`modernize-redundant-void-arg `, "Yes" :doc:`modernize-replace-auto-ptr `, "Yes" + :doc:`modernize-replace-memcpy-with-std-copy `, "Yes" :doc:`modernize-replace-disallow-copy-and-assign-macro `, "Yes" :doc:`modernize-replace-random-shuffle `, "Yes" :doc:`modernize-return-braced-init-list `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-replace-memcpy-with-stdcopy.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-replace-memcpy-with-stdcopy.rst new file mode 100644 index 0000000000000..922a7f36e7e07 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-replace-memcpy-with-stdcopy.rst @@ -0,0 +1,47 @@ +.. title:: clang-tidy - modernize-replace-memcpy-with-stdcopy + +modernize-replace-memcpy-with-stdcopy +=================================== + +Replaces all occurrences of the C ``memcpy`` function with ``std::copy`` + +Example: + +.. code-block:: c++ + + /*! + * \param destination Pointer to the destination array where the content is to be copied + * \param source Pointer to the source of data to be copied + * \param num Number of bytes to copy + */ + memcpy(destination, source, num); + +becomes + +.. code-block:: c++ + + /*! + * \param destination Pointer to the destination array where the content is to be copied + * \param source Pointer to the source of data to be copied + * \param num Number of bytes to copy + */ + std::copy(source, source + (num / sizeof *source), destination); + +Bytes to iterator conversion +---------------------------- + +Unlike ``std::copy`` that take an iterator on the last element of the source array, ``memcpy`` request the number of bytes to copy. +In order to make the check working, it will convert the size parameter to an iterator by replacing it by ``source + (num / sizeof *source)`` + +Header inclusion +---------------- + +``std::copy`` being provided by the ``algorithm`` header file, this check will include it if needed. + +Options +------- + +.. option:: IncludeStyle + + A string specifying which include-style is used, `llvm` or `google`. Default + is `llvm`. diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-memcpy-with-stdcopy.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-memcpy-with-stdcopy.rst new file mode 100644 index 0000000000000..8708fe3b6e665 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-memcpy-with-stdcopy.rst @@ -0,0 +1,47 @@ +.. title:: clang-tidy - modernize-replace-memcpy-with-std-copy + +modernize-replace-memcpy-with-std-copy +=================================== + +Replaces all occurrences of the C ``memcpy`` function with ``std::copy`` + +Example: + +.. code-block:: c++ + + /*! + * \param destination Pointer to the destination array where the content is to be copied + * \param source Pointer to the source of data to be copied + * \param num Number of bytes to copy + */ + memcpy(destination, source, num); + +becomes + +.. code-block:: c++ + + /*! + * \param destination Pointer to the destination array where the content is to be copied + * \param source Pointer to the source of data to be copied + * \param num Number of bytes to copy + */ + std::copy(source, source + (num / sizeof *source), destination); + +Bytes to iterator conversion +---------------------------- + +Unlike ``std::copy`` that take an iterator on the last element of the source array, ``memcpy`` request the number of bytes to copy. +In order to make the check working, it will convert the size parameter to an iterator by replacing it by ``source + (num / sizeof *source)`` + +Header inclusion +---------------- + +``std::copy`` being provided by the ``algorithm`` header file, this check will include it if needed. + +Options +------- + +.. option:: IncludeStyle + + A string specifying which include-style is used, `llvm` or `google`. Default + is `llvm`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-memcpy-with-std-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-memcpy-with-std-copy.cpp new file mode 100644 index 0000000000000..ce07fc5801da6 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-memcpy-with-std-copy.cpp @@ -0,0 +1,146 @@ +// RUN: %check_clang_tidy %s modernize-replace-memcpy-with-std-copy %t + +// CHECK-FIXES: #include + +namespace { + +using size_t = decltype(sizeof(int)); + +namespace std { +typedef long long int64_t; +typedef short int16_t; +typedef char int8_t; + +void *memcpy(void *__restrict dest, const void *__restrict src, size_t n); + +template struct vector { + vector(size_t); + + T *data(); + size_t size() const; + void resize(size_t); + using value_type = T; +}; + +size_t size(void *); + +size_t strlen(const char *); +} // namespace std + +void *memcpy(void *__restrict dest, const void *__restrict src, size_t n); +} // namespace + +void notSupportedEx() { + char source[] = "once upon a daydream...", dest[4]; + + auto *primitiveDest = new std::int8_t; + std::memcpy(primitiveDest, source, sizeof primitiveDest); + + auto *primitiveDest2 = new std::int16_t; + std::memcpy(primitiveDest2, source, sizeof primitiveDest); + std::memcpy(primitiveDest2, source, sizeof primitiveDest2); + + double d = 0.1; + std::int64_t n; + // don't warn on calls over non-sequences + std::memcpy(&n, &d, sizeof d); + + // object creation in destination buffer + struct S { + int x{42}; + void *operator new(size_t, void *) noexcept { return nullptr; } + } s; + alignas(S) char buf[sizeof(S)]; + S *ps = new (buf) S; // placement new + // // don't warn on calls over non-sequences + std::memcpy(ps, &s, sizeof s); + + const char *pSource = "once upon a daydream..."; + char *pDest = new char[4]; + std::memcpy(dest, pSource, sizeof dest); + std::memcpy(pDest, source, 4); +} + +void noFixItEx() { + char source[] = "once upon a daydream...", dest[4]; + + // no FixIt when return value is used + auto *ptr = std::memcpy(dest, source, sizeof dest); + // CHECK-MESSAGES: [[@LINE-1]]:20: warning: prefer std::copy_n to memcpy + + std::vector vec_i16(4); + // not a supported type, should be a sequence of bytes, otherwise it is difficult to compute the n in copy_n + std::memcpy(vec_i16.data(), source, + vec_i16.size() * sizeof(decltype(vec_i16)::value_type)); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy +} + +void sequenceOfBytesEx() { + // the check should support memcpy conversion for the following types: + // T[] + // std::vector + // std::span + // std::deque + // std::array + // std::string + // std::string_view + // where T is byte-like + + char source[] = "once upon a daydream...", dest[4]; + std::memcpy(dest, source, sizeof dest); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy + // CHECK-FIXES: std::copy_n(std::begin(source), std::size(dest), std::begin(dest)); + + // __jm__ warn on global call as well + memcpy(dest, source, sizeof dest); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: prefer std::copy_n to memcpy + // CHECK-FIXES: std::copy_n(std::begin(source), std::size(dest), std::begin(dest)); + + std::vector vec_i8(4); + std::memcpy(vec_i8.data(), source, vec_i8.size()); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy + // CHECK-FIXES: std::copy_n(std::begin(source), std::size(vec_i8), std::begin(vec_i8)); + + // __jm__ make configurable whether stl containers should use members or free fns. + // __jm__ for now use free fns. only + + std::memcpy(dest, vec_i8.data(), vec_i8.size()); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy + // CHECK-FIXES: std::copy_n(std::begin(vec_i8), std::size(vec_i8), std::begin(dest)); + std::memcpy(dest, vec_i8.data(), sizeof(dest)); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy + // CHECK-FIXES: std::copy_n(vec_i8.data(), std::size(dest), std::begin(dest)); + std::memcpy(dest, vec_i8.data(), std::size(dest)); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy + // CHECK-FIXES: std::copy_n(std::begin(source), std::size(vec_i8), std::begin(vec_i8)); + + std::memcpy(dest, vec_i8.data(), 1 + vec_i8.size() / 2); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy + // CHECK-FIXES: std::copy_n(std::begin(vec_i8), 1 + std::size(vec_i8) / 2, std::begin(dest)); + std::memcpy(dest, vec_i8.data(), 1 + sizeof(dest) / 2); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy + // CHECK-FIXES: std::copy_n(vec_i8.data(), 1 + std::size(dest) / 2, std::begin(dest)); + std::memcpy(dest, vec_i8.data(), 1 + std::size(dest) / 2); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy + // CHECK-FIXES: std::copy_n(std::begin(source), 1 + std::size(dest) / 2, std::begin(vec_i8)); +} + +// void uninitialized_copy_ex() { +// std::vector v = {"This", "is", "an", "example"}; + +// std::string* p; +// std::size_t sz; +// std::tie(p, sz) = std::get_temporary_buffer(v.size()); +// sz = std::min(sz, v.size()); + +// std::uninitialized_copy_n(v.begin(), sz, p); + +// for (std::string* i = p; i != p + sz; ++i) +// { +// std::cout << *i << ' '; +// i->~basic_string(); +// } +// std::cout << '\n'; + +// std::return_temporary_buffer(p); +// } \ No newline at end of file