[clang-tools-extra] [clang-tidy][modernize] Replace memmove/memcpy with std::copy (PR #122940)
Kuba MigdaĆ via cfe-commits
cfe-commits at lists.llvm.org
Sat Feb 8 10:23:44 PST 2025
https://github.com/kidq330 updated https://github.com/llvm/llvm-project/pull/122940
>From 1a36fbf8c89b30b6c7c807b0da219d0b81553173 Mon Sep 17 00:00:00 2001
From: Giovanni Martins <giovannimartins2000 at gmail.com>
Date: Wed, 6 Dec 2023 11:26:53 -0300
Subject: [PATCH 01/15] replace memcpy with std::copy on clang-tidy
removed typo on files
sort imports
removed some typo
solve linter reports
update modernize-replace-memcpy-with-stdcopy.rst
---
.../clang-tidy/modernize/CMakeLists.txt | 1 +
.../modernize/ModernizeTidyModule.cpp | 3 +
.../modernize/ReplaceMemcpyWithStdCopy.cpp | 119 ++++++++++++++++++
.../modernize/ReplaceMemcpyWithStdCopy.h | 49 ++++++++
clang-tools-extra/docs/ReleaseNotes.rst | 5 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../modernize-replace-memcpy-with-stdcopy.rst | 47 +++++++
7 files changed, 225 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-replace-memcpy-with-stdcopy.rst
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index bab1167fb15ff20..e67c583a5f17392 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 fc46c72982fdce8..413b85c89ae4cb8 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"
@@ -94,6 +95,8 @@ class ModernizeModule : public ClangTidyModule {
"modernize-replace-auto-ptr");
CheckFactories.registerCheck<ReplaceDisallowCopyAndAssignMacroCheck>(
"modernize-replace-disallow-copy-and-assign-macro");
+ CheckFactories.registerCheck<ReplaceMemcpyWithStdCopy>(
+ "modernize-replace-memcpy-by-stdcopy");
CheckFactories.registerCheck<ReplaceRandomShuffleCheck>(
"modernize-replace-random-shuffle");
CheckFactories.registerCheck<ReturnBracedInitListCheck>(
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 000000000000000..af6b365c162517e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp
@@ -0,0 +1,119 @@
+//===--- 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/OptionsUtils.h"
+#include <array>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+ReplaceMemcpyWithStdCopy::ReplaceMemcpyWithStdCopy(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
+ utils::IncludeSorter::IS_LLVM)) {
+}
+
+void ReplaceMemcpyWithStdCopy::registerMatchers(MatchFinder *Finder) {
+ assert(Finder != nullptr);
+
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ auto MemcpyMatcher =
+ callExpr(hasDeclaration(functionDecl(hasName("memcpy"),
+ isExpansionInSystemHeader())),
+ isExpansionInMainFile())
+ .bind("memcpy_function");
+
+ Finder->addMatcher(MemcpyMatcher, this);
+}
+
+void ReplaceMemcpyWithStdCopy::registerPPCallbacks(
+ const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Inserter =
+ std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
+ IncludeStyle);
+ PP->addPPCallbacks(Inserter->CreatePPCallbacks());
+}
+
+void ReplaceMemcpyWithStdCopy::check(const MatchFinder::MatchResult &Result) {
+ const auto *MemcpyNode = Result.Nodes.getNodeAs<CallExpr>("memcpy_function");
+ assert(MemcpyNode != nullptr);
+
+ DiagnosticBuilder Diag =
+ diag(MemcpyNode->getExprLoc(), "use std::copy instead of memcpy");
+
+ renameFunction(Diag, MemcpyNode);
+ reorderArgs(Diag, MemcpyNode);
+ insertHeader(Diag, MemcpyNode, Result.SourceManager);
+}
+
+void ReplaceMemcpyWithStdCopy::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle",
+ utils::IncludeSorter::toString(IncludeStyle));
+}
+
+void ReplaceMemcpyWithStdCopy::renameFunction(DiagnosticBuilder &Diag,
+ const CallExpr *MemcpyNode) {
+ const CharSourceRange FunctionNameSourceRange = CharSourceRange::getCharRange(
+ MemcpyNode->getBeginLoc(), MemcpyNode->getArg(0)->getBeginLoc());
+
+ Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, "std::copy(");
+}
+
+void ReplaceMemcpyWithStdCopy::reorderArgs(DiagnosticBuilder &Diag,
+ const CallExpr *MemcpyNode) {
+ std::array<std::string, 3> 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]);
+}
+
+void ReplaceMemcpyWithStdCopy::insertHeader(DiagnosticBuilder &Diag,
+ const CallExpr *MemcpyNode,
+ SourceManager *const SM) {
+ Optional<FixItHint> FixInclude = Inserter->CreateIncludeInsertion(
+ /*FileID=*/SM->getMainFileID(), /*Header=*/"algorithm",
+ /*IsAngled=*/true);
+ if (FixInclude)
+ Diag << *FixInclude;
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
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 000000000000000..0f262bf839af24a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h
@@ -0,0 +1,49 @@
+//===--- 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 <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace 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 check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+
+private:
+ void renameFunction(DiagnosticBuilder &Diag, const CallExpr *MemcpyNode);
+ void reorderArgs(DiagnosticBuilder &Diag, const CallExpr *MemcpyNode);
+ void insertHeader(DiagnosticBuilder &Diag, const CallExpr *MemcpyNode,
+ SourceManager *const SM);
+
+private:
+ std::unique_ptr<utils::IncludeInserter> Inserter;
+ utils::IncludeInserter IncludeInserter;
+ const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#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 35cb3e387e4e644..552044356e404eb 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -319,6 +319,11 @@ Changes in existing checks
member function calls too and to only expand macros starting with ``PRI``
and ``__PRI`` from ``<inttypes.h>`` in the format string.
+- New :doc:`modernize-replace-memcpy-with-stdcopy
+ <clang-tidy/checks/modernize-replace-memcpy-by-stdcopy>` check.
+
+ Replaces all occurrences of the C ``memcpy`` function by ``std::copy``.
+
- Improved :doc:`modernize-use-std-print
<clang-tidy/checks/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 e8f9b4e829634be..8af5fc8fa3cfaee 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -287,6 +287,7 @@ Clang-Tidy Checks
:doc:`modernize-raw-string-literal <modernize/raw-string-literal>`, "Yes"
:doc:`modernize-redundant-void-arg <modernize/redundant-void-arg>`, "Yes"
:doc:`modernize-replace-auto-ptr <modernize/replace-auto-ptr>`, "Yes"
+ :doc:`modernize-replace-memcpy-with-std-copy <modernize/replace-auto-ptr>`, "Yes"
:doc:`modernize-replace-disallow-copy-and-assign-macro <modernize/replace-disallow-copy-and-assign-macro>`, "Yes"
:doc:`modernize-replace-random-shuffle <modernize/replace-random-shuffle>`, "Yes"
:doc:`modernize-return-braced-init-list <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 000000000000000..922a7f36e7e078f
--- /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`.
>From 3cb944c4989b135d1d2c3f488174dcd4f338c80a Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Sat, 19 Oct 2024 16:49:27 +0200
Subject: [PATCH 02/15] Add unit tests
---
.../replace-memcpy-with-std-copy.cpp | 146 ++++++++++++++++++
1 file changed, 146 insertions(+)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/replace-memcpy-with-std-copy.cpp
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 000000000000000..ce07fc5801da6a6
--- /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 <algorithm>
+
+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 <typename T> 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<std::int16_t> 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<T>
+ // std::span<T>
+ // std::deque<T>
+ // std::array<T, _>
+ // 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<char> 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<std::string> v = {"This", "is", "an", "example"};
+
+// std::string* p;
+// std::size_t sz;
+// std::tie(p, sz) = std::get_temporary_buffer<std::string>(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<char>();
+// }
+// std::cout << '\n';
+
+// std::return_temporary_buffer(p);
+// }
\ No newline at end of file
>From 416739af6767f40693e78c9af029851c3bbfbea1 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Mon, 23 Dec 2024 22:02:46 +0100
Subject: [PATCH 03/15] Implement dest/src arg matchers
(commit serves as proof of work for course credit :)) )
[skip ci]
---
.../clang-tidy/modernize/CMakeLists.txt | 2 +-
.../modernize/ModernizeTidyModule.cpp | 12 +-
.../modernize/ReplaceMemcpyWithStdCopy.cpp | 119 ------
.../modernize/ReplaceMemcpyWithStdCopy.h | 49 ---
.../modernize/ReplaceWithStdCopyCheck.cpp | 393 ++++++++++++++++++
.../modernize/ReplaceWithStdCopyCheck.h | 82 ++++
clang-tools-extra/docs/ReleaseNotes.rst | 4 +-
.../docs/clang-tidy/checks/list.rst | 2 +-
.../modernize-replace-memcpy-with-stdcopy.rst | 4 +-
.../modernize/replace-memcpy-with-stdcopy.rst | 47 +++
.../replace-memcpy-with-std-copy.cpp | 146 -------
.../modernize/replace-with-std-copy.cpp | 320 ++++++++++++++
12 files changed, 854 insertions(+), 326 deletions(-)
delete mode 100644 clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp
delete mode 100644 clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h
create mode 100644 clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/replace-memcpy-with-stdcopy.rst
delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/replace-memcpy-with-std-copy.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index e67c583a5f17392..67eaf01eb915566 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -23,7 +23,7 @@ add_clang_library(clangTidyModernizeModule STATIC
RedundantVoidArgCheck.cpp
ReplaceAutoPtrCheck.cpp
ReplaceDisallowCopyAndAssignMacroCheck.cpp
- ReplaceMemcpyWithStdCopy.cpp
+ ReplaceWithStdCopyCheck.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 413b85c89ae4cb8..0c2c351cc71eb40 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -24,8 +24,8 @@
#include "RedundantVoidArgCheck.h"
#include "ReplaceAutoPtrCheck.h"
#include "ReplaceDisallowCopyAndAssignMacroCheck.h"
-#include "ReplaceMemcpyWithStdCopy.h"
#include "ReplaceRandomShuffleCheck.h"
+#include "ReplaceWithStdCopyCheck.h"
#include "ReturnBracedInitListCheck.h"
#include "ShrinkToFitCheck.h"
#include "TypeTraitsCheck.h"
@@ -95,8 +95,8 @@ class ModernizeModule : public ClangTidyModule {
"modernize-replace-auto-ptr");
CheckFactories.registerCheck<ReplaceDisallowCopyAndAssignMacroCheck>(
"modernize-replace-disallow-copy-and-assign-macro");
- CheckFactories.registerCheck<ReplaceMemcpyWithStdCopy>(
- "modernize-replace-memcpy-by-stdcopy");
+ CheckFactories.registerCheck<ReplaceWithStdCopyCheck>(
+ "modernize-replace-with-stdcopy");
CheckFactories.registerCheck<ReplaceRandomShuffleCheck>(
"modernize-replace-random-shuffle");
CheckFactories.registerCheck<ReturnBracedInitListCheck>(
@@ -113,11 +113,11 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
"modernize-use-default-member-init");
CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
- CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default");
+ CheckFactories.registerCheck<UseEqualsDefaultCheck>(
+ "modernize-use-equals-default");
CheckFactories.registerCheck<UseEqualsDeleteCheck>(
"modernize-use-equals-delete");
- CheckFactories.registerCheck<UseNodiscardCheck>(
- "modernize-use-nodiscard");
+ CheckFactories.registerCheck<UseNodiscardCheck>("modernize-use-nodiscard");
CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp
deleted file mode 100644
index af6b365c162517e..000000000000000
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-//===--- 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/OptionsUtils.h"
-#include <array>
-
-using namespace clang;
-using namespace clang::ast_matchers;
-
-namespace clang {
-namespace tidy {
-namespace modernize {
-
-ReplaceMemcpyWithStdCopy::ReplaceMemcpyWithStdCopy(StringRef Name,
- ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context),
- IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
- utils::IncludeSorter::IS_LLVM)) {
-}
-
-void ReplaceMemcpyWithStdCopy::registerMatchers(MatchFinder *Finder) {
- assert(Finder != nullptr);
-
- if (!getLangOpts().CPlusPlus)
- return;
-
- auto MemcpyMatcher =
- callExpr(hasDeclaration(functionDecl(hasName("memcpy"),
- isExpansionInSystemHeader())),
- isExpansionInMainFile())
- .bind("memcpy_function");
-
- Finder->addMatcher(MemcpyMatcher, this);
-}
-
-void ReplaceMemcpyWithStdCopy::registerPPCallbacks(
- const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
- if (!getLangOpts().CPlusPlus)
- return;
-
- Inserter =
- std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
- IncludeStyle);
- PP->addPPCallbacks(Inserter->CreatePPCallbacks());
-}
-
-void ReplaceMemcpyWithStdCopy::check(const MatchFinder::MatchResult &Result) {
- const auto *MemcpyNode = Result.Nodes.getNodeAs<CallExpr>("memcpy_function");
- assert(MemcpyNode != nullptr);
-
- DiagnosticBuilder Diag =
- diag(MemcpyNode->getExprLoc(), "use std::copy instead of memcpy");
-
- renameFunction(Diag, MemcpyNode);
- reorderArgs(Diag, MemcpyNode);
- insertHeader(Diag, MemcpyNode, Result.SourceManager);
-}
-
-void ReplaceMemcpyWithStdCopy::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "IncludeStyle",
- utils::IncludeSorter::toString(IncludeStyle));
-}
-
-void ReplaceMemcpyWithStdCopy::renameFunction(DiagnosticBuilder &Diag,
- const CallExpr *MemcpyNode) {
- const CharSourceRange FunctionNameSourceRange = CharSourceRange::getCharRange(
- MemcpyNode->getBeginLoc(), MemcpyNode->getArg(0)->getBeginLoc());
-
- Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, "std::copy(");
-}
-
-void ReplaceMemcpyWithStdCopy::reorderArgs(DiagnosticBuilder &Diag,
- const CallExpr *MemcpyNode) {
- std::array<std::string, 3> 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]);
-}
-
-void ReplaceMemcpyWithStdCopy::insertHeader(DiagnosticBuilder &Diag,
- const CallExpr *MemcpyNode,
- SourceManager *const SM) {
- Optional<FixItHint> FixInclude = Inserter->CreateIncludeInsertion(
- /*FileID=*/SM->getMainFileID(), /*Header=*/"algorithm",
- /*IsAngled=*/true);
- if (FixInclude)
- Diag << *FixInclude;
-}
-
-} // namespace modernize
-} // namespace tidy
-} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h
deleted file mode 100644
index 0f262bf839af24a..000000000000000
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//===--- 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 <memory>
-#include <string>
-#include <vector>
-
-namespace clang {
-namespace tidy {
-namespace 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 check(const ast_matchers::MatchFinder::MatchResult &Result) override;
- void storeOptions(ClangTidyOptions::OptionMap &Options) override;
-
-private:
- void renameFunction(DiagnosticBuilder &Diag, const CallExpr *MemcpyNode);
- void reorderArgs(DiagnosticBuilder &Diag, const CallExpr *MemcpyNode);
- void insertHeader(DiagnosticBuilder &Diag, const CallExpr *MemcpyNode,
- SourceManager *const SM);
-
-private:
- std::unique_ptr<utils::IncludeInserter> Inserter;
- utils::IncludeInserter IncludeInserter;
- const utils::IncludeSorter::IncludeStyle IncludeStyle;
-};
-
-} // namespace modernize
-} // namespace tidy
-} // namespace clang
-
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_MEMCPY_WITH_STDCOPY_H
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
new file mode 100644
index 000000000000000..beba705f5a141fd
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
@@ -0,0 +1,393 @@
+//===--- ReplaceWithStdCopyCheck.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 "ReplaceWithStdCopyCheck.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/StringRef.h"
+#include <variant>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+namespace clang::tidy {
+
+template <>
+struct OptionEnumMapping<
+ enum modernize::ReplaceWithStdCopyCheck::FlaggableCallees> {
+ static llvm::ArrayRef<std::pair<
+ modernize::ReplaceWithStdCopyCheck::FlaggableCallees, StringRef>>
+ getEnumMapping() {
+ static constexpr std::pair<
+ modernize::ReplaceWithStdCopyCheck::FlaggableCallees, StringRef>
+ Mapping[] = {
+ {modernize::ReplaceWithStdCopyCheck::FlaggableCallees::
+ MemmoveAndMemcpy,
+ "MemmoveAndMemcpy"},
+ {modernize::ReplaceWithStdCopyCheck::FlaggableCallees::OnlyMemmove,
+ "OnlyMemmove"},
+ };
+ return {Mapping};
+ }
+};
+
+template <>
+struct OptionEnumMapping<
+ enum modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle> {
+ static llvm::ArrayRef<std::pair<
+ modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle, StringRef>>
+ getEnumMapping() {
+ static constexpr std::pair<
+ modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle, StringRef>
+ Mapping[] = {
+ {modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle::
+ FreeFunc,
+ "FreeFunc"},
+ {modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle::
+ MemberCall,
+ "MemberCall"},
+ };
+ return {Mapping};
+ }
+};
+
+namespace modernize {
+namespace {
+namespace arg {
+namespace tag {
+struct VariantPtrArgRef {
+ llvm::StringLiteral AsContainer;
+ llvm::StringLiteral AsCArray;
+ llvm::StringLiteral Fallback;
+};
+
+struct PtrArgRefs {
+ VariantPtrArgRef VariantRefs;
+ llvm::StringLiteral CharType;
+};
+
+struct DestArg {
+ static constexpr PtrArgRefs Refs = {
+ {"DestArg::AsContainer", "DestArg::AsCArray", "DestArg::Fallback"},
+ "DestArg::CharType"};
+};
+struct SourceArg {
+ static constexpr PtrArgRefs Refs = {
+ {"SourceArg::AsContainer", "SourceArg::AsCArray", "SourceArg::Fallback"},
+ "SourceArg::CharType"
+
+ };
+};
+struct SizeArg {};
+} // namespace tag
+
+template <bool IsConst> auto createPtrArgMatcher(tag::PtrArgRefs Refs) {
+ auto [VariantRefs, ValueTypeRef] = Refs;
+ auto [AsContainer, AsCArray] = VariantRefs;
+
+ auto AllowedContainerNamesM = []() {
+ if constexpr (IsConst) {
+ return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
+ "::std::vector", "::std::basic_string");
+ } else {
+ return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
+ "::std::vector", "::std::basic_string",
+ "::std::basic_string_view", "::std::span");
+ }
+ }();
+
+ auto ValueTypeM = type().bind(ValueTypeRef);
+
+ auto AllowedContainerTypeM = hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(recordDecl(classTemplateSpecializationDecl(
+ AllowedContainerNamesM,
+ hasTemplateArgument(
+ 0, templateArgument(refersToType(
+ hasUnqualifiedDesugaredType(ValueTypeM)))))))));
+
+ auto VariantContainerM =
+ expr(hasType(AllowedContainerTypeM)).bind(AsContainer);
+ auto VariantCArrayM =
+ expr(hasType(arrayType(hasElementType(ValueTypeM)))).bind(AsCArray);
+
+ auto StdDataReturnM = returns(pointerType(pointee(
+ hasUnqualifiedDesugaredType(equalsBoundNode(ValueTypeRef.str())))));
+
+ auto StdDataMemberDeclM =
+ cxxMethodDecl(hasName("data"), parameterCountIs(0), StdDataReturnM);
+ // ,unless(isConst()) // __jm__ ONLY difference between Dest
+ // and Source calls, but maybe don't include it?
+
+ auto StdDataFreeDeclM = functionDecl(
+ hasName("::std::data"), parameterCountIs(1),
+ StdDataReturnM); // __jm__ possibly elaborate on argument type here?
+
+ auto StdDataMemberCallM = cxxMemberCallExpr(
+ callee(StdDataMemberDeclM), argumentCountIs(0),
+ on(expr(hasType(AllowedContainerTypeM)).bind(AsContainer)));
+
+ auto ArrayOrContainerM = expr(anyOf(VariantCArrayM, VariantContainerM));
+
+ auto StdDataFreeCallM = callExpr(callee(StdDataFreeDeclM), argumentCountIs(1),
+ hasArgument(0, ArrayOrContainerM));
+
+ return expr(anyOf(StdDataMemberCallM, StdDataFreeCallM, VariantCArrayM));
+}
+
+template <typename TAG> class MatcherLinker : public TAG {
+public:
+ static auto createMatcher();
+ static void handleResult(const MatchFinder::MatchResult &Result);
+
+private:
+ using AllowedTAG = std::enable_if_t<std::is_same_v<TAG, tag::DestArg> ||
+ std::is_same_v<TAG, tag::SourceArg> ||
+ std::is_same_v<TAG, tag::SizeArg>>;
+};
+
+template <> auto MatcherLinker<tag::DestArg>::createMatcher() {
+ return createPtrArgMatcher<false>(Refs);
+}
+
+template <> auto MatcherLinker<tag::SourceArg>::createMatcher() {
+ return createPtrArgMatcher<true>(Refs);
+}
+
+template <> auto MatcherLinker<tag::SizeArg>::createMatcher() {
+ // cases:
+ // 1. call to std::size
+ // 2. member call to .size()
+ // 3. sizeof node or sizeof(node) (c-array)
+ // 4. N * sizeof(type) (hints at sequence-related use, however none singled out)
+
+ // need a robust way of exchanging information between matchers that will help in sequence identification
+ // I see one possiblity
+ // size "thinks" dest is rawptr with pointee sequence
+ // N * sizeof
+ // size "thinks" src is rawptr with pointee sequence
+ // size agrees dest is container/c-array
+ // size agrees src is container/c-array
+
+ return anything(); // __jm__ TODO
+}
+
+using Dest = MatcherLinker<tag::DestArg>;
+using Source = MatcherLinker<tag::SourceArg>;
+using Size = MatcherLinker<tag::SizeArg>;
+} // namespace arg
+
+constexpr llvm::StringLiteral ExpressionRef = "::std::memcpy";
+constexpr llvm::StringLiteral StdCopyHeader = "<algorithm>";
+constexpr llvm::StringLiteral ReturnValueDiscardedRef =
+ "ReturnValueDiscardedRef";
+
+// 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<QualType> &TypeMatcher) {
+ return anyOf(hasType(TypeMatcher),
+ hasType(pointerType(pointee(TypeMatcher))));
+}
+
+} // namespace
+
+ReplaceWithStdCopyCheck::ReplaceWithStdCopyCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ Inserter(Options.getLocalOrGlobal("IncludeStyle",
+ utils::IncludeSorter::IS_LLVM),
+ areDiagsSelfContained()),
+ FlaggableCallees_(Options.getLocalOrGlobal("FlaggableCallees",
+ FlaggableCalleesDefault)),
+ StdCallsReplacementStyle_(Options.getLocalOrGlobal(
+ "StdCallsReplacementStyle", StdCallsReplacementStyleDefault)) {}
+
+void ReplaceWithStdCopyCheck::registerMatchers(MatchFinder *Finder) {
+ const auto ReturnValueUsedM =
+ hasParent(compoundStmt().bind(ReturnValueDiscardedRef));
+
+ const auto OffendingDeclM =
+ functionDecl(parameterCountIs(3), hasAnyName(getFlaggableCallees()));
+
+ // cases:
+ // 1. One of the arguments is definitely a sequence and the other a pointer -
+ // match
+ // 2. Both source and dest are pointers, but size is of the form ((N :=
+ // expr()) * sizeof(bytelike())) - match (false positive if N \in {0, 1})
+
+ using namespace arg;
+ const auto Expression =
+ callExpr(callee(OffendingDeclM),
+ anyOf(optionally(ReturnValueUsedM),
+ allOf(hasArgument(0, Dest::createMatcher()),
+ hasArgument(1, Source::createMatcher()),
+ hasArgument(2, Size::createMatcher()))));
+ Finder->addMatcher(Expression.bind(ExpressionRef), this);
+}
+
+void ReplaceWithStdCopyCheck::registerPPCallbacks(
+ const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
+ Inserter.registerPreprocessor(PP);
+}
+
+void ReplaceWithStdCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle", Inserter.getStyle());
+ Options.store(Opts, "FlaggableCallees", FlaggableCallees_);
+ Options.store(Opts, "StdCallsReplacementStyle", StdCallsReplacementStyle_);
+}
+
+#include "llvm/Support/Debug.h"
+#define DEBUG_TYPE "clang-tidy"
+
+void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto &CallNode = *Result.Nodes.getNodeAs<CallExpr>(ExpressionRef);
+
+ auto Diag = diag(CallNode.getExprLoc(), "prefer std::copy_n to %0")
+ << cast<NamedDecl>(CallNode.getCalleeDecl());
+
+ tryIssueFixIt(Result, Diag, CallNode);
+
+ {
+ // 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
+}
+
+// void ReplaceWithStdCopyCheck::handleMemcpy(const CallExpr &Node) {
+
+// // FixIt
+// const CharSourceRange FunctionNameSourceRange =
+// CharSourceRange::getCharRange(
+// Node.getBeginLoc(), Node.getArg(0)->getBeginLoc());
+
+// Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
+// "std::copy_n(");
+// }
+
+// bool ReplaceWithStdCopyCheck::fixItPossible(
+// const MatchFinder::MatchResult &Result) {}
+
+void ReplaceWithStdCopyCheck::tryIssueFixIt(
+ const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag,
+ const CallExpr &CallNode) {
+ // don't issue a fixit if the result of the call is used
+ if (bool IsReturnValueUsed =
+ Result.Nodes.getNodeAs<Stmt>(ReturnValueDiscardedRef) == nullptr;
+ IsReturnValueUsed)
+ return;
+
+ // to make sure we can issue a FixIt, need to be pretty sure we're dealing
+ // with a sequence and it is a byte sequence
+ // 1. For now we are sure both source and dest are sequences, but not
+ // necessarily of bytes
+ // 2. We can relax conditions to where just one arg is a sequence, and the
+ // other can then be a sequence or raw pointer
+ // 3. when both dest and source are pointers (no expectations on the argument
+ // as everything required is enforced by the type system) we can use a
+ // heuristic using the form of the 3rd argument expression
+ //
+
+ // delete the memmove/memcpy
+ // insert an std::copy_n
+ struct CArrayTag {};
+ struct ContainerTag {};
+ struct RawPtrTag {};
+ using PtrArgVariant = std::variant<CArrayTag, ContainerTag, RawPtrTag>;
+ struct PtrArg {
+ PtrArgVariant Tag;
+ const Expr *Node;
+ };
+
+ const auto MakePtrArg = [&](arg::tag::VariantPtrArgRef Refs) -> PtrArg {
+ if (const auto *Node =
+ Result.Nodes.getNodeAs<Expr>(arg::Dest::Refs.VariantRefs.AsCArray);
+ Node != nullptr) {
+ // freestanding std::begin
+ return {CArrayTag{}, Node};
+ }
+ if (const auto *Node = Result.Nodes.getNodeAs<Expr>(
+ arg::Dest::Refs.VariantRefs.AsContainer);
+ Node != nullptr) {
+ return {ContainerTag{}, Node};
+ }
+ const auto *Node =
+ Result.Nodes.getNodeAs<Expr>(arg::Dest::Refs.VariantRefs.Fallback);
+ return {RawPtrTag{}, Node};
+ };
+
+ auto Dest = MakePtrArg(arg::Dest::Refs.VariantRefs);
+ auto Source = MakePtrArg(arg::Source::Refs.VariantRefs);
+
+ if (std::holds_alternative<RawPtrTag>(Dest.Tag) and
+ std::holds_alternative<RawPtrTag>(Source.Tag) and CheckSizeArgPermitsFix()) {
+
+ } else {
+
+ }
+
+ Diag << Inserter.createIncludeInsertion(
+ Result.SourceManager->getFileID(CallNode.getBeginLoc()), StdCopyHeader);
+}
+
+void ReplaceWithStdCopyCheck::reorderArgs(DiagnosticBuilder &Diag,
+ const CallExpr *MemcpyNode) {
+ std::array<std::string, 3> 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]);
+}
+
+llvm::ArrayRef<StringRef> ReplaceWithStdCopyCheck::getFlaggableCallees() const {
+ switch (FlaggableCallees_) {
+ case FlaggableCallees::OnlyMemmove:
+ return {"::memmove", "::std::memmove"};
+ case FlaggableCallees::MemmoveAndMemcpy:
+ return {"::memmove", "::std::memmove", "::memcpy", "::std::memcpy"};
+ }
+}
+} // namespace modernize
+} // namespace clang::tidy
\ No newline at end of file
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h
new file mode 100644
index 000000000000000..16510174c1ee552
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h
@@ -0,0 +1,82 @@
+//===--- 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_WITH_STDCOPY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_WITH_STDCOPY_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/IncludeInserter.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/Basic/LangOptions.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang::tidy::modernize {
+
+// Replace C-style calls to functions like memmove and memcpy with analogous
+// calls to std::copy or std::copy_n, depending on the context
+class ReplaceWithStdCopyCheck : public ClangTidyCheck {
+public:
+ enum class FlaggableCallees {
+ OnlyMemmove,
+ MemmoveAndMemcpy,
+ };
+
+ enum class StdCallsReplacementStyle {
+ FreeFunc,
+ MemberCall,
+ };
+
+ ReplaceWithStdCopyCheck(StringRef Name, ClangTidyContext *Context);
+ ~ReplaceWithStdCopyCheck() 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<TraversalKind> 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:
+ void tryIssueFixIt(const ast_matchers::MatchFinder::MatchResult &Result,
+ const DiagnosticBuilder &Diag, const CallExpr &CallNode);
+ llvm::ArrayRef<StringRef> getFlaggableCallees() const;
+
+ utils::IncludeInserter Inserter;
+ // __jm__ TODO options:
+ // 1. list of functions that can be replaced, memmove by default,
+ // and memcpy as opt-in
+ // 2. should calls to {begin,end,size} be replaced
+ // with their members or free fns.
+
+ static constexpr auto FlaggableCalleesDefault = FlaggableCallees::OnlyMemmove;
+ static constexpr auto StdCallsReplacementStyleDefault =
+ StdCallsReplacementStyle::FreeFunc;
+
+ const FlaggableCallees FlaggableCallees_;
+ const StdCallsReplacementStyle StdCallsReplacementStyle_;
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_WITH_STDCOPY_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 552044356e404eb..94e1ea1a7238640 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -319,8 +319,8 @@ Changes in existing checks
member function calls too and to only expand macros starting with ``PRI``
and ``__PRI`` from ``<inttypes.h>`` in the format string.
-- New :doc:`modernize-replace-memcpy-with-stdcopy
- <clang-tidy/checks/modernize-replace-memcpy-by-stdcopy>` check.
+- New :doc:`modernize-replace-with-stdcopy
+ <clang-tidy/checks/modernize-replace-with-stdcopy>` check.
Replaces all occurrences of the C ``memcpy`` function by ``std::copy``.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 8af5fc8fa3cfaee..0ee04dc15082b04 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -287,7 +287,7 @@ Clang-Tidy Checks
:doc:`modernize-raw-string-literal <modernize/raw-string-literal>`, "Yes"
:doc:`modernize-redundant-void-arg <modernize/redundant-void-arg>`, "Yes"
:doc:`modernize-replace-auto-ptr <modernize/replace-auto-ptr>`, "Yes"
- :doc:`modernize-replace-memcpy-with-std-copy <modernize/replace-auto-ptr>`, "Yes"
+ :doc:`modernize-replace-with-std-copy <modernize/replace-with-std-copy>`, "Yes"
:doc:`modernize-replace-disallow-copy-and-assign-macro <modernize/replace-disallow-copy-and-assign-macro>`, "Yes"
:doc:`modernize-replace-random-shuffle <modernize/replace-random-shuffle>`, "Yes"
:doc:`modernize-return-braced-init-list <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
index 922a7f36e7e078f..74276c37bc2c094 100644
--- 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
@@ -1,6 +1,6 @@
-.. title:: clang-tidy - modernize-replace-memcpy-with-stdcopy
+.. title:: clang-tidy - modernize-with-stdcopy
-modernize-replace-memcpy-with-stdcopy
+modernize-with-stdcopy
===================================
Replaces all occurrences of the C ``memcpy`` function with ``std::copy``
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 000000000000000..119a3184208c2aa
--- /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-with-stdcopy
+
+modernize-replace-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/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
deleted file mode 100644
index ce07fc5801da6a6..000000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-memcpy-with-std-copy.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-// RUN: %check_clang_tidy %s modernize-replace-memcpy-with-std-copy %t
-
-// CHECK-FIXES: #include <algorithm>
-
-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 <typename T> 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<std::int16_t> 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<T>
- // std::span<T>
- // std::deque<T>
- // std::array<T, _>
- // 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<char> 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<std::string> v = {"This", "is", "an", "example"};
-
-// std::string* p;
-// std::size_t sz;
-// std::tie(p, sz) = std::get_temporary_buffer<std::string>(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<char>();
-// }
-// std::cout << '\n';
-
-// std::return_temporary_buffer(p);
-// }
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
new file mode 100644
index 000000000000000..3d0dc1805167688
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
@@ -0,0 +1,320 @@
+// RUN: %check_clang_tidy %s modernize-replace-with-std-copy %t
+
+// possible call scenarios, infeasible to cover all
+// replacement type:
+// [ ] members when possible
+// [X] all free
+// source:
+// type:
+// [ ] T[]
+// std::data:
+// [ ] free
+// [ ] N/A
+// [ ] container
+// std::data:
+// [ ] free
+// [ ] member
+// type:
+// [ ] std::vector<T>
+// [ ] std::span<T>
+// [ ] std::deque<T>
+// [ ] std::array<T, _>
+// [ ] std::string
+// [ ] std::string_view
+// dest:
+// type:
+// [ ] T[]
+// std::data:
+// [ ] free
+// [ ] N/A
+// [ ] container
+// std::data:
+// [ ] free
+// [ ] member
+// type:
+// [ ] std::vector<T>
+// [ ] std::span<T>
+// [ ] std::deque<T>
+// [ ] std::array<T, _>
+// [ ] std::string
+// [ ] std::string_view
+// callee:
+// name:
+// [ ] memmove
+// [ ] memcpy
+// [ ] wmemmove
+// [ ] wmemcpy
+// qualified:
+// [ ] y
+// [ ] n
+
+// CHECK-FIXES: #include <algorithm>
+
+namespace {
+
+using size_t = decltype(sizeof(int));
+
+namespace std {
+using int64_t = long long ;
+using int32_t = int ;
+using int16_t = short ;
+using int8_t = char ;
+
+using char32 = int32_t;
+using char16 = int16_t;
+using char8 = int8_t;
+
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T, typename A>
+struct basic_string {
+ typedef basic_string<C, T, A> _Type;
+ basic_string();
+ basic_string(const C* p, const A& a = A());
+
+ const C* c_str() const;
+ const C* data() const;
+
+ _Type& append(const C* s);
+ _Type& append(const C* s, size_t n);
+ _Type& assign(const C* s);
+ _Type& assign(const C* s, size_t n);
+
+ int compare(const _Type&) const;
+ int compare(const C* s) const;
+ int compare(size_t pos, size_t len, const _Type&) const;
+ int compare(size_t pos, size_t len, const C* s) const;
+
+ size_t find(const _Type& str, size_t pos = 0) const;
+ size_t find(const C* s, size_t pos = 0) const;
+ size_t find(const C* s, size_t pos, size_t n) const;
+
+ _Type& insert(size_t pos, const _Type& str);
+ _Type& insert(size_t pos, const C* s);
+ _Type& insert(size_t pos, const C* s, size_t n);
+
+ _Type& operator+=(const _Type& str);
+ _Type& operator+=(const C* s);
+ _Type& operator=(const _Type& str);
+ _Type& operator=(const C* s);
+};
+
+using string = basic_string<char, std::char_traits<char>, std::allocator<char>>;
+using wstring = basic_string<wchar_t, std::char_traits<wchar_t>,std::allocator<wchar_t>>;
+using u16string = basic_string<char16, std::char_traits<char16>, std::allocator<char16>>;
+using u32string = basic_string<char32, std::char_traits<char32>, std::allocator<char32>>;
+
+
+
+void *memcpy(void *__restrict dest, const void *__restrict src, size_t n);
+void *memmove(void *__restrict dest, const void *__restrict src, size_t n);
+
+template <typename T> struct vector {
+ vector(size_t);
+
+ T *data();
+ const T *data() const;
+ size_t size() const;
+ void resize(size_t);
+ using value_type = T;
+};
+
+template<typename T>
+T* data(vector<T>&);
+
+template<typename T>
+T* data(T[]);
+
+template<typename T>
+const T* data(const vector<T>&);
+
+template<typename T>
+const T* data(const T[]);
+
+size_t size(void *);
+
+size_t strlen(const char *);
+} // namespace std
+
+void *memcpy(void *__restrict dest, const void *__restrict src, size_t n);
+void *memmove(void *__restrict dest, const void *__restrict src, size_t n);
+} // namespace
+
+
+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 StructType {
+ int X{42};
+ void *operator new(size_t, void *) noexcept { return nullptr; }
+ } Struct;
+ alignas(StructType) char Buf[sizeof(StructType)];
+ StructType *Ps = new (Buf) StructType; // placement new
+ // // don't warn on calls over non-sequences
+ std::memcpy(Ps, &Struct, sizeof Struct);
+
+ const char *PtrSource = "once upon a daydream...";
+ char *PtrDest = new char[4];
+ std::memcpy(Dest, PtrSource, sizeof Dest);
+ std::memcpy(PtrDest, Source, 4);
+}
+
+void noFixItEx() {
+ {
+ int Source[10];
+ std::vector<char> Dest(5);
+
+ memmove(Source, std::data(Dest), 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: prefer std::copy_n to memcpy
+ std::memmove(Source, std::data(Dest), 1);
+ memmove(std::data(Source), std::data(Dest), 1);
+ std::memmove(std::data(Source), std::data(Dest), 1);
+ memmove(std::data(Source), std::data(Dest), 1);
+ std::memmove(std::data(Source), std::data(Dest), 1);
+ }
+ char Source[] = "once upon a daydream...", Dest[4];
+
+ // no FixIt when return value is used
+
+ [](auto){}(std::memcpy(Dest, Source, sizeof Dest));
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: prefer std::copy_n to memcpy
+
+ std::vector<std::int16_t> VecI16(4);
+ // not a supported type, should be a sequence of bytes, otherwise it is difficult to compute the n in copy_n
+ std::memcpy(VecI16.data(), Source,
+ VecI16.size() * sizeof(decltype(VecI16)::value_type));
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+
+ std::memcpy(std::data(VecI16), Source,
+ VecI16.size() * sizeof(decltype(VecI16)::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<T>
+ // std::span<T>
+ // std::deque<T>
+ // std::array<T, _>
+ // std::string
+ // std::string_view
+ // where T is byte-like
+
+ {
+ char Source[] = "once upon a daydream...";
+ char 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));
+
+ std::memcpy(std::data(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));
+
+ std::memcpy(Dest, std::data(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));
+
+ std::memcpy(std::data(Dest), std::data(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));
+
+ memcpy(std::data(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));
+
+ memcpy(Dest, std::data(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));
+
+ memcpy(std::data(Dest), std::data(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));
+ }
+
+ {
+ char Source[] = "once upon a daydream...";
+ std::vector<char> Dest(4);
+
+ std::memcpy(Dest.data(), Source, Dest.size());
+ // 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));
+
+ std::memcpy(std::data(Dest), Source, Dest.size());
+ // 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));
+ }
+
+ {
+ std::vector<char> Source(10);
+ char Dest[4];
+
+ std::memcpy(Dest, Source.data(), Source.size());
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ // CHECK-FIXES: std::copy_n(std::begin(Source), std::size(Source), std::begin(Dest));
+ std::memcpy(Dest, Source.data(), sizeof(Dest));
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ // CHECK-FIXES: std::copy_n(Source.data(), std::size(Dest), std::begin(Dest));
+ std::memcpy(Dest, Source.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(Source), std::begin(Source));
+
+ std::memcpy(Dest, Source.data(), 1 + Source.size() / 2);
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ // CHECK-FIXES: std::copy_n(std::begin(Source), 1 + std::size(Source) / 2, std::begin(Dest));
+ std::memcpy(Dest, Source.data(), 1 + sizeof(Dest) / 2);
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ // CHECK-FIXES: std::copy_n(Source.data(), 1 + std::size(Dest) / 2, std::begin(Dest));
+ std::memcpy(Dest, Source.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(Source));
+
+ std::memcpy(Dest, std::data(Source), Source.size());
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ // CHECK-FIXES: std::copy_n(std::begin(Source), std::size(Source), std::begin(Dest));
+ std::memcpy(Dest, std::data(Source), sizeof(Dest));
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ // CHECK-FIXES: std::copy_n(Source.data(), std::size(Dest), std::begin(Dest));
+ std::memcpy(Dest, std::data(Source), 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(Source), std::begin(Source));
+
+ std::memcpy(Dest, std::data(Source), 1 + Source.size() / 2);
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ // CHECK-FIXES: std::copy_n(std::begin(Source), 1 + std::size(Source) / 2, std::begin(Dest));
+ std::memcpy(Dest, std::data(Source), 1 + sizeof(Dest) / 2);
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ // CHECK-FIXES: std::copy_n(Source.data(), 1 + std::size(Dest) / 2, std::begin(Dest));
+ std::memcpy(Dest, std::data(Source), 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(Source));
+ }
+
+ // __jm__ make configurable whether stl containers should use members or free fns.
+ // __jm__ for now use free fns. only
+
+}
+} // namespace
\ No newline at end of file
>From 6d8c0ffc589a80d2830ed0975eb6eb3306d0d458 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Sun, 29 Dec 2024 22:43:17 +0100
Subject: [PATCH 04/15] pre debug WIP, ditch raw pointer support for now i
[skip ci]
---
.../modernize/ReplaceWithStdCopyCheck.cpp | 623 ++++++++++++------
.../modernize/ReplaceWithStdCopyCheck.h | 9 -
2 files changed, 404 insertions(+), 228 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
index beba705f5a141fd..b594018675989fc 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
@@ -7,9 +7,14 @@
//===----------------------------------------------------------------------===//
#include "ReplaceWithStdCopyCheck.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/StringRef.h"
#include <variant>
@@ -37,72 +42,37 @@ struct OptionEnumMapping<
}
};
-template <>
-struct OptionEnumMapping<
- enum modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle> {
- static llvm::ArrayRef<std::pair<
- modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle, StringRef>>
- getEnumMapping() {
- static constexpr std::pair<
- modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle, StringRef>
- Mapping[] = {
- {modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle::
- FreeFunc,
- "FreeFunc"},
- {modernize::ReplaceWithStdCopyCheck::StdCallsReplacementStyle::
- MemberCall,
- "MemberCall"},
- };
- return {Mapping};
- }
-};
-
namespace modernize {
namespace {
-namespace arg {
-namespace tag {
-struct VariantPtrArgRef {
- llvm::StringLiteral AsContainer;
- llvm::StringLiteral AsCArray;
- llvm::StringLiteral Fallback;
-};
-struct PtrArgRefs {
- VariantPtrArgRef VariantRefs;
- llvm::StringLiteral CharType;
-};
+constexpr llvm::StringLiteral ExpressionRef = "::std::memcpy";
+constexpr llvm::StringLiteral StdCopyHeader = "<algorithm>";
+constexpr llvm::StringLiteral ReturnValueDiscardedRef =
+ "ReturnValueDiscardedRef";
-struct DestArg {
- static constexpr PtrArgRefs Refs = {
- {"DestArg::AsContainer", "DestArg::AsCArray", "DestArg::Fallback"},
- "DestArg::CharType"};
-};
-struct SourceArg {
- static constexpr PtrArgRefs Refs = {
- {"SourceArg::AsContainer", "SourceArg::AsCArray", "SourceArg::Fallback"},
- "SourceArg::CharType"
+namespace ptrarg {
- };
+struct Refs {
+ llvm::StringLiteral AsContainer;
+ llvm::StringLiteral AsCArray;
+ size_t FallbackParameterIdx;
+ llvm::StringLiteral ValueType;
};
-struct SizeArg {};
-} // namespace tag
-template <bool IsConst> auto createPtrArgMatcher(tag::PtrArgRefs Refs) {
- auto [VariantRefs, ValueTypeRef] = Refs;
- auto [AsContainer, AsCArray] = VariantRefs;
+template <typename RefsT> auto createPtrArgMatcher() {
+ constexpr Refs Refs = RefsT::Refs;
auto AllowedContainerNamesM = []() {
- if constexpr (IsConst) {
+ if constexpr (true)
return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
"::std::vector", "::std::basic_string");
- } else {
+ else
return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
"::std::vector", "::std::basic_string",
"::std::basic_string_view", "::std::span");
- }
}();
- auto ValueTypeM = type().bind(ValueTypeRef);
+ auto ValueTypeM = type().bind(Refs.ValueType);
auto AllowedContainerTypeM = hasUnqualifiedDesugaredType(
recordType(hasDeclaration(recordDecl(classTemplateSpecializationDecl(
@@ -112,12 +82,12 @@ template <bool IsConst> auto createPtrArgMatcher(tag::PtrArgRefs Refs) {
hasUnqualifiedDesugaredType(ValueTypeM)))))))));
auto VariantContainerM =
- expr(hasType(AllowedContainerTypeM)).bind(AsContainer);
+ expr(hasType(AllowedContainerTypeM)).bind(Refs.AsContainer);
auto VariantCArrayM =
- expr(hasType(arrayType(hasElementType(ValueTypeM)))).bind(AsCArray);
+ expr(hasType(arrayType(hasElementType(ValueTypeM)))).bind(Refs.AsCArray);
auto StdDataReturnM = returns(pointerType(pointee(
- hasUnqualifiedDesugaredType(equalsBoundNode(ValueTypeRef.str())))));
+ hasUnqualifiedDesugaredType(equalsBoundNode(Refs.ValueType.str())))));
auto StdDataMemberDeclM =
cxxMethodDecl(hasName("data"), parameterCountIs(0), StdDataReturnM);
@@ -130,71 +100,173 @@ template <bool IsConst> auto createPtrArgMatcher(tag::PtrArgRefs Refs) {
auto StdDataMemberCallM = cxxMemberCallExpr(
callee(StdDataMemberDeclM), argumentCountIs(0),
- on(expr(hasType(AllowedContainerTypeM)).bind(AsContainer)));
+ on(expr(hasType(AllowedContainerTypeM)).bind(Refs.AsContainer)));
auto ArrayOrContainerM = expr(anyOf(VariantCArrayM, VariantContainerM));
auto StdDataFreeCallM = callExpr(callee(StdDataFreeDeclM), argumentCountIs(1),
hasArgument(0, ArrayOrContainerM));
- return expr(anyOf(StdDataMemberCallM, StdDataFreeCallM, VariantCArrayM));
+ // the last expr() in anyOf assumes previous matchers are ran eagerly from
+ // left to right, still need to test this is the actual behaviour
+ return expr(
+ anyOf(StdDataMemberCallM, StdDataFreeCallM, VariantCArrayM, expr()));
}
-template <typename TAG> class MatcherLinker : public TAG {
-public:
- static auto createMatcher();
- static void handleResult(const MatchFinder::MatchResult &Result);
-
-private:
- using AllowedTAG = std::enable_if_t<std::is_same_v<TAG, tag::DestArg> ||
- std::is_same_v<TAG, tag::SourceArg> ||
- std::is_same_v<TAG, tag::SizeArg>>;
+namespace tag {
+struct Container {};
+struct CArray {};
+struct RawPtr {};
+} // namespace tag
+struct PtrArg {
+ std::variant<tag::Container, tag::CArray, tag::RawPtr> Tag;
+ const Expr *Node;
};
-template <> auto MatcherLinker<tag::DestArg>::createMatcher() {
- return createPtrArgMatcher<false>(Refs);
+template <typename RefT>
+auto extractNode(const CallExpr &CallNode,
+ const MatchFinder::MatchResult &Result) -> PtrArg {
+ constexpr Refs Refs = RefT::Refs;
+ if (const auto *Node = Result.Nodes.getNodeAs<Expr>(Refs.AsCArray))
+ return {tag::CArray{}, Node};
+ if (const auto *Node = Result.Nodes.getNodeAs<Expr>(Refs.AsContainer))
+ return {tag::Container{}, Node};
+ return {tag::RawPtr{}, CallNode.getArg(Refs.FallbackParameterIdx)};
}
-template <> auto MatcherLinker<tag::SourceArg>::createMatcher() {
- return createPtrArgMatcher<true>(Refs);
+template <typename RefT>
+QualType extractValueType(const MatchFinder::MatchResult &Result) {
+ return *Result.Nodes.getNodeAs<QualType>(RefT::Refs.ValueType);
}
+} // namespace ptrarg
+
+namespace dst {
+constexpr size_t argIndex = 0;
+struct RefT {
+ static constexpr ptrarg::Refs Refs = {
+ "Dst::AsContainer",
+ "Dst::AsCArray",
+ 0,
+ "Dst::ValueType",
+ };
+};
-template <> auto MatcherLinker<tag::SizeArg>::createMatcher() {
- // cases:
- // 1. call to std::size
- // 2. member call to .size()
- // 3. sizeof node or sizeof(node) (c-array)
- // 4. N * sizeof(type) (hints at sequence-related use, however none singled out)
-
- // need a robust way of exchanging information between matchers that will help in sequence identification
- // I see one possiblity
- // size "thinks" dest is rawptr with pointee sequence
- // N * sizeof
- // size "thinks" src is rawptr with pointee sequence
- // size agrees dest is container/c-array
- // size agrees src is container/c-array
-
- return anything(); // __jm__ TODO
+auto createMatcher() { return ptrarg::createPtrArgMatcher<RefT>(); }
+auto extractNode(const CallExpr &CallNode,
+ const MatchFinder::MatchResult &Result) {
+ return ptrarg::extractNode<RefT>(CallNode, Result);
+}
+auto extractValueType(const MatchFinder::MatchResult &Result) {
+ return ptrarg::extractValueType<RefT>(Result);
}
+} // namespace dst
-using Dest = MatcherLinker<tag::DestArg>;
-using Source = MatcherLinker<tag::SourceArg>;
-using Size = MatcherLinker<tag::SizeArg>;
-} // namespace arg
+namespace src {
+constexpr size_t argIndex = 1;
-constexpr llvm::StringLiteral ExpressionRef = "::std::memcpy";
-constexpr llvm::StringLiteral StdCopyHeader = "<algorithm>";
-constexpr llvm::StringLiteral ReturnValueDiscardedRef =
- "ReturnValueDiscardedRef";
+struct SrcRefsT {
+ static constexpr ptrarg::Refs Refs = {
+ "Src::AsContainer",
+ "Src::AsCArray",
+ 1,
+ "Src::ValueType",
+ };
+};
+
+auto createMatcher() { return ptrarg::createPtrArgMatcher<SrcRefsT>(); }
+auto extractNode(const CallExpr &CallNode,
+ const MatchFinder::MatchResult &Result) {
+ return ptrarg::extractNode<SrcRefsT>(CallNode, Result);
+}
+auto extractValueType(const MatchFinder::MatchResult &Result) {
+ return ptrarg::extractValueType<SrcRefsT>(Result);
+}
+} // namespace src
+
+namespace size {
+constexpr size_t argIndex = 2;
+// cases:
+// 1. sizeof(T) where T is a type
+// 2. sizeof(rec) where rec is a CArray
+// 3. N * sizeof(T)
+// 4. strlen(rec) [+ 1]
+// 5. other expr
+namespace variant {
+struct SizeOfExpr {
+ const Expr *Arg;
+};
+struct NSizeOfExpr {
+ const Expr *N;
+ const Expr *Arg;
+};
+struct Strlen {
+ const Expr *Arg;
+};
+} // namespace variant
+using SizeArg = std::variant<variant::SizeOfExpr, variant::NSizeOfExpr,
+ variant::Strlen, const Expr *>;
+
+struct SizeComponents {
+ CharUnits Unit;
+ std::string NExpr;
+};
+
+struct IArg {
+ virtual ~IArg() = default;
+ virtual SizeComponents extractSizeComponents() = 0;
+};
-// 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<QualType> &TypeMatcher) {
- return anyOf(hasType(TypeMatcher),
- hasType(pointerType(pointee(TypeMatcher))));
+static constexpr struct Refs {
+ llvm::StringLiteral SizeOfArg;
+ llvm::StringLiteral N;
+ llvm::StringLiteral StrlenArg;
+} Refs = {
+ "Size::SizeOfExpr",
+ "Size::N",
+ "Size::StrlenArg",
+};
+
+auto createMatcher() {
+ auto NSizeOfT =
+ binaryOperator(hasOperatorName("*"),
+ hasOperands(expr().bind(Refs.N),
+ sizeOfExpr(has(expr().bind(Refs.SizeOfArg)))));
+ auto Strlen = callExpr(callee(functionDecl(hasAnyName(
+ "::strlen", "::std::strlen", "::wcslen",
+ "::std::wcslen", "::strnlen_s", "::std::strnlen_s",
+ "::wcsnlen_s", "::std::wcsnlen_s"))),
+ hasArgument(0, expr().bind(Refs.StrlenArg)));
+ auto StrlenPlusOne = binaryOperator(
+ hasOperatorName("+"), hasOperands(Strlen, integerLiteral(equals(1))));
+
+ auto SizeOfExpr = sizeOfExpr(hasArgumentOfType(type().bind(Refs.SizeOfArg)));
+
+ return expr(anyOf(NSizeOfT, Strlen, StrlenPlusOne, SizeOfExpr));
+}
+
+SizeArg extractNode(const CallExpr &CallNode,
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *SizeOfArgNode = Result.Nodes.getNodeAs<Expr>(Refs.SizeOfArg);
+ SizeOfArgNode != nullptr) {
+ if (const auto *NNode = Result.Nodes.getNodeAs<Expr>(Refs.N);
+ NNode != nullptr)
+ return variant::NSizeOfExpr{NNode, SizeOfArgNode};
+ return variant::SizeOfExpr{SizeOfArgNode};
+ }
+ if (const auto *StrlenArgNode = Result.Nodes.getNodeAs<Expr>(Refs.StrlenArg);
+ StrlenArgNode != nullptr) {
+ return variant::Strlen{StrlenArgNode};
+ }
+ return CallNode.getArg(2);
}
+// 1. N * sizeof(type) (commutative) - issue warning but no fixit, or fixit
+// only if both src/dest are fixit friendly 1.1. This might allow fixits for
+// wider-than-byte element collections
+// 2. strlen(src|dest) ?(+ 1 (commutative)) -- issue warning but no fixit
+// 4. sizeof(variable) only if that variable is of arrayType, if it's of
+// ptrType that may indicate [de]serialization
+
+} // namespace size
} // namespace
@@ -205,9 +277,7 @@ ReplaceWithStdCopyCheck::ReplaceWithStdCopyCheck(StringRef Name,
utils::IncludeSorter::IS_LLVM),
areDiagsSelfContained()),
FlaggableCallees_(Options.getLocalOrGlobal("FlaggableCallees",
- FlaggableCalleesDefault)),
- StdCallsReplacementStyle_(Options.getLocalOrGlobal(
- "StdCallsReplacementStyle", StdCallsReplacementStyleDefault)) {}
+ FlaggableCalleesDefault)) {}
void ReplaceWithStdCopyCheck::registerMatchers(MatchFinder *Finder) {
const auto ReturnValueUsedM =
@@ -217,18 +287,17 @@ void ReplaceWithStdCopyCheck::registerMatchers(MatchFinder *Finder) {
functionDecl(parameterCountIs(3), hasAnyName(getFlaggableCallees()));
// cases:
- // 1. One of the arguments is definitely a sequence and the other a pointer -
- // match
+ // 1. One of the arguments is definitely a collection and the other a pointer
+ // - match
// 2. Both source and dest are pointers, but size is of the form ((N :=
// expr()) * sizeof(bytelike())) - match (false positive if N \in {0, 1})
- using namespace arg;
const auto Expression =
callExpr(callee(OffendingDeclM),
anyOf(optionally(ReturnValueUsedM),
- allOf(hasArgument(0, Dest::createMatcher()),
- hasArgument(1, Source::createMatcher()),
- hasArgument(2, Size::createMatcher()))));
+ allOf(hasArgument(0, dst::createMatcher()),
+ hasArgument(1, src::createMatcher()),
+ hasArgument(2, size::createMatcher()))));
Finder->addMatcher(Expression.bind(ExpressionRef), this);
}
@@ -240,7 +309,6 @@ void ReplaceWithStdCopyCheck::registerPPCallbacks(
void ReplaceWithStdCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "FlaggableCallees", FlaggableCallees_);
- Options.store(Opts, "StdCallsReplacementStyle", StdCallsReplacementStyle_);
}
#include "llvm/Support/Debug.h"
@@ -249,144 +317,261 @@ void ReplaceWithStdCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
const auto &CallNode = *Result.Nodes.getNodeAs<CallExpr>(ExpressionRef);
- auto Diag = diag(CallNode.getExprLoc(), "prefer std::copy_n to %0")
- << cast<NamedDecl>(CallNode.getCalleeDecl());
+ auto dstVT = dst::extractValueType(Result);
+ auto srcVT = src::extractValueType(Result);
- tryIssueFixIt(Result, Diag, CallNode);
+ // basis for converting size argument to std::copy_n's when issuing fixit
+ auto valueTypeWidth = Result.Context->getTypeSizeInChars(dstVT);
- {
- // 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
-}
+ // Don't report cases where value type widths differ, as this might indicate
+ // [de]serialization and hard to reason about replacements using std::copy[_n]
+ if (valueTypeWidth != Result.Context->getTypeSizeInChars(srcVT))
+ return;
-// void ReplaceWithStdCopyCheck::handleMemcpy(const CallExpr &Node) {
+ auto dst = dst::extractNode(CallNode, Result);
+ auto src = src::extractNode(CallNode, Result);
+ auto size = size::extractNode(CallNode, Result);
+
+ // only have this function return a non-empty optional if the form of the size
+ // argument strongly indicates collection-related usage
+ auto exprAsString = [&](const Expr *Node) {
+ return Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Node->getSourceRange()),
+ *Result.SourceManager, getLangOpts())
+ .str();
+ };
-// // FixIt
-// const CharSourceRange FunctionNameSourceRange =
-// CharSourceRange::getCharRange(
-// Node.getBeginLoc(), Node.getArg(0)->getBeginLoc());
+ auto extractComponents = [&]() -> std::optional<size::SizeComponents> {
+ if (const auto *NSizeOfExprNode =
+ std::get_if<size::variant::NSizeOfExpr>(&size);
+ NSizeOfExprNode != nullptr) {
+ auto &[N, Arg] = *NSizeOfExprNode;
+ auto sizeOfArgWidth = Result.Context->getTypeSizeInChars(Arg->getType());
+ return {{sizeOfArgWidth, exprAsString(N)}};
+ }
+ if (const auto *StrlenNode = std::get_if<size::variant::Strlen>(&size);
+ StrlenNode != nullptr) {
+ auto strlenArgTypeWidth = Result.Context->getTypeSizeInChars(
+ StrlenNode->Arg->getType()->getPointeeType());
+ return {{strlenArgTypeWidth, exprAsString(CallNode.getArg(2))}};
+ }
+ return std::nullopt;
+ };
-// Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
-// "std::copy_n(");
-// }
+ if (auto maybeComponents = extractComponents(); maybeComponents.has_value()) {
+ auto [unit, nExpr] = *maybeComponents;
+ if (unit != valueTypeWidth) {
+ // __jm__ tricky to offer a helpful fixit here, as the size argument
+ // doesn't seem to be related to the pointee types of src and dst
+ return;
+ }
+ // __jm__ makes sense to assume
+ }
+ // might be a reinterpretation call, only other case where we don't flag
+ if (std::holds_alternative<ptrarg::tag::RawPtr>(dst.Tag) and
+ std::holds_alternative<ptrarg::tag::RawPtr>(src.Tag)) {
+ // not supported yet, need to come up with a robust heuristic first
+ return;
+ }
-// bool ReplaceWithStdCopyCheck::fixItPossible(
-// const MatchFinder::MatchResult &Result) {}
+ // both src and dst are collection expressions, enough to wrap them in
+ // std::begin calls
+ auto Diag = diag(CallNode.getExprLoc(), "prefer std::copy_n to %0")
+ << cast<NamedDecl>(CallNode.getCalleeDecl());
-void ReplaceWithStdCopyCheck::tryIssueFixIt(
- const MatchFinder::MatchResult &Result, const DiagnosticBuilder &Diag,
- const CallExpr &CallNode) {
// don't issue a fixit if the result of the call is used
if (bool IsReturnValueUsed =
Result.Nodes.getNodeAs<Stmt>(ReturnValueDiscardedRef) == nullptr;
IsReturnValueUsed)
return;
- // to make sure we can issue a FixIt, need to be pretty sure we're dealing
- // with a sequence and it is a byte sequence
- // 1. For now we are sure both source and dest are sequences, but not
- // necessarily of bytes
- // 2. We can relax conditions to where just one arg is a sequence, and the
- // other can then be a sequence or raw pointer
- // 3. when both dest and source are pointers (no expectations on the argument
- // as everything required is enforced by the type system) we can use a
- // heuristic using the form of the 3rd argument expression
- //
+ Diag << FixItHint::CreateRemoval(CallNode.getSourceRange());
+ std::string srcFixit = "std::cbegin(" + exprAsString(src.Node) + ")";
+ std::string dstFixit = "std::begin(" + exprAsString(dst.Node) + ")";
- // delete the memmove/memcpy
- // insert an std::copy_n
- struct CArrayTag {};
- struct ContainerTag {};
- struct RawPtrTag {};
- using PtrArgVariant = std::variant<CArrayTag, ContainerTag, RawPtrTag>;
- struct PtrArg {
- PtrArgVariant Tag;
- const Expr *Node;
- };
+ auto calleeIsWideVariant =
+ CallNode.getDirectCallee()->getParamDecl(0)->getType()->isWideCharType();
- const auto MakePtrArg = [&](arg::tag::VariantPtrArgRef Refs) -> PtrArg {
- if (const auto *Node =
- Result.Nodes.getNodeAs<Expr>(arg::Dest::Refs.VariantRefs.AsCArray);
- Node != nullptr) {
- // freestanding std::begin
- return {CArrayTag{}, Node};
+ auto calleeUnit = [&]() {
+ if (calleeIsWideVariant) {
+ return Result.Context->getTypeSizeInChars(
+ (Result.Context->getWideCharType()));
}
- if (const auto *Node = Result.Nodes.getNodeAs<Expr>(
- arg::Dest::Refs.VariantRefs.AsContainer);
- Node != nullptr) {
- return {ContainerTag{}, Node};
+ return CharUnits::One();
+ }();
+ auto sizeFixit = [&]() -> std::string {
+ if (valueTypeWidth == calleeUnit) {
+ return exprAsString(CallNode.getArg(size::argIndex));
}
- const auto *Node =
- Result.Nodes.getNodeAs<Expr>(arg::Dest::Refs.VariantRefs.Fallback);
- return {RawPtrTag{}, Node};
- };
-
- auto Dest = MakePtrArg(arg::Dest::Refs.VariantRefs);
- auto Source = MakePtrArg(arg::Source::Refs.VariantRefs);
-
- if (std::holds_alternative<RawPtrTag>(Dest.Tag) and
- std::holds_alternative<RawPtrTag>(Source.Tag) and CheckSizeArgPermitsFix()) {
-
- } else {
-
- }
+ // try to factor out the unit from the size expression
+ if (auto maybeSizeComponents = extractComponents();
+ maybeSizeComponents.has_value() and
+ maybeSizeComponents->Unit == valueTypeWidth) {
+ return maybeSizeComponents->NExpr;
+ }
+ // last resort, divide by valueTypeWidth
+ return exprAsString(CallNode.getArg(size::argIndex)) + " / " + "sizeof(" +
+ dstVT.getAsString() + ")";
+ }();
- Diag << Inserter.createIncludeInsertion(
- Result.SourceManager->getFileID(CallNode.getBeginLoc()), StdCopyHeader);
-}
+ Diag << FixItHint::CreateInsertion(CallNode.getEndLoc(),
+ "std::copy_n(" + srcFixit + ", " +
+ sizeFixit + ", " + dstFixit + ");");
+
+ // if (const auto *NSizeofExprNode =
+ // std::get_if<size::variant::NSizeOfExpr>(&size);
+ // NSizeofExprNode != nullptr) {
+ // auto &[N, Arg] = *NSizeofExprNode;
+ // auto sizeOfArgWidth = Result.Context->getTypeSizeInChars(Arg->getType());
+ // issueFixitIfWidthsMatch(dst.Node, src.Node,
+ // {sizeOfArgWidth, exprAsString(N)});
+ // return;
+ // }
+ // if (const auto *StrlenNode = std::get_if<size::variant::Strlen>(&size);
+ // StrlenNode != nullptr) {
+ // auto strlenArgTypeWidth = Result.Context->getTypeSizeInChars(
+ // StrlenNode->Arg->getType()->getPointeeType());
+ // issueFixitIfWidthsMatch(
+ // dst.Node, src.Node,
+ // {strlenArgTypeWidth, exprAsString(CallNode.getArg(2))});
+ // return;
+ // }
+ // if (const auto *SizeOfExprNode =
+ // std::get_if<size::variant::SizeOfExpr>(&size);
+ // SizeOfExprNode != nullptr) {
+ // auto &Arg = SizeOfExprNode->Arg;
+ // if (SizeOfExprNode->Arg->getType()->isArrayType()) {
+ // issueFixitIfWidthsMatch(
+ // dst.Node, src.Node,
+ // {CharUnits::One(), exprAsString(CallNode.getArg(2))});
+ // return;
+ // }
+ // // __jm__ weird bc we assume dst and src are collections
+ // // if Arg turns out to have the same type as dst or src then just suggest
+ // // copy via the assignment operator
+ // if (auto argType = Arg->getType(); argType == dstVT or argType == srcVT)
+ // {
+ // issueFixit{valueTypeWidth, exprAsString(CallNode.getArg(2))};
+ // return;
+ // }
+ // // __jm__ only flag this as suspicious with a further explanation in the
+ // // diagnostic TODO: a sizeof of an unrelated type when copying between
+ // // collections does not make a lot of sense
+
+ // auto sizeDRE = [&]() -> const DeclRefExpr * {
+ // if (const auto *Variant = std::get_if<size::variant::Strlen>(&size);
+ // Variant != nullptr) {
+ // return llvm::dyn_cast_if_present<DeclRefExpr>(Variant->Arg);
+ // }
+ // if (const auto *Variant =
+ // std::get_if<size::variant::SizeOfExpr>(&size);
+ // Variant != nullptr) {
+ // return llvm::dyn_cast_if_present<DeclRefExpr>(Variant->Arg);
+ // }
+ // if (const auto *Variant =
+ // std::get_if<size::variant::NSizeOfExpr>(&size);
+ // Variant != nullptr) {
+ // return llvm::dyn_cast_if_present<DeclRefExpr>(Variant->Arg);
+ // }
+ // return nullptr;
+ // }();
+
+ // one thing the analysis of the size argument must return is an Expr* Node
+ // that we can lift into std::copy_n's third argument
+
+ // 1. strlen(DeclRefExpr) also hints that the referenced variable is a
+ // collection
+
+ // 2. sizeof(expr)
+ // 2.1. expr is a c-array DeclRefExpr to one of src/dst, copy_n size
+ // becomes std::size(expr)
+ // 2.2. both src and dst are not raw pointers and expr is a type of width
+ // equal to vtw, essentialy fallthrough to 3
+
+ // 3. N * sizeof(expr) is okay when expr is a type with width ==
+ // valueTypeWidth and N may be verbatim lifted into copy_n's third argument
-void ReplaceWithStdCopyCheck::reorderArgs(DiagnosticBuilder &Diag,
- const CallExpr *MemcpyNode) {
- std::array<std::string, 3> Arg;
+ // to make sure we can issue a FixIt, need to be pretty sure we're dealing
+ // with a collection and it is a byte collection
+ // 1. For now we are sure both source and dest are collections, but not
+ // necessarily of bytes
+ // 2. We can relax conditions to where just one arg is a collection, and the
+ // other can then be a collection or raw pointer. However, this is not
+ // robust as there are cases where this may be used for unmarshalling
+ // 3. when both dest and source are pointers (no expectations on the
+ // argument as everything required is enforced by the type system) we can
+ // use a heuristic using the form of the 3rd argument expression
+ //
- LangOptions LangOpts;
- LangOpts.CPlusPlus = true;
- PrintingPolicy Policy(LangOpts);
+ // delete the memmove/memcpy
+ // insert an std::copy_n
- // 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);
+ // using PtrArgVariant = std::variant<CArrayTag, ContainerTag, RawPtrTag>;
+ // struct PtrArg {
+ // PtrArgVariant Tag;
+ // const Expr *Node;
+ // };
+
+ // const auto MakePtrArg = [&](arg::tag::VariantPtrArgRef Refs) -> PtrArg {
+ // if (const auto *Node = Result.Nodes.getNodeAs<Expr>(
+ // arg::Dest::Refs.VariantRefs.AsCArray);
+ // Node != nullptr) {
+ // // freestanding std::begin
+ // return {CArrayTag{}, Node};
+ // }
+ // if (const auto *Node = Result.Nodes.getNodeAs<Expr>(
+ // arg::Dest::Refs.VariantRefs.AsContainer);
+ // Node != nullptr) {
+ // return {ContainerTag{}, Node};
+ // }
+ // const auto *Node = CallNode.getArg(Refs.FallbackParameterIdx);
+ // return {RawPtrTag{}, Node};
+ // };
+
+ // auto Dest = MakePtrArg(arg::Dest::Refs.VariantRefs);
+ // auto Source = MakePtrArg(arg::Source::Refs.VariantRefs);
+
+ // if (std::holds_alternative<RawPtrTag>(Dest.Tag) and
+ // std::holds_alternative<RawPtrTag>(Source.Tag) and
+ // CheckSizeArgPermitsFix()) {
+
+ // } else {
+ // }
+
+ Diag << Inserter.createIncludeInsertion(
+ Result.SourceManager->getFileID(CallNode.getBeginLoc()), StdCopyHeader);
+ {
+ // 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());
}
-
- // 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]);
+ // dest source size -> source size dest
}
llvm::ArrayRef<StringRef> ReplaceWithStdCopyCheck::getFlaggableCallees() const {
switch (FlaggableCallees_) {
case FlaggableCallees::OnlyMemmove:
- return {"::memmove", "::std::memmove"};
+ return {"::memmove", "::std::memmove", "::wmemmove", "::std::wmemmove"};
case FlaggableCallees::MemmoveAndMemcpy:
- return {"::memmove", "::std::memmove", "::memcpy", "::std::memcpy"};
+ return {"::memmove", "::std::memmove", "::memcpy", "::std::memcpy",
+ "::wmemmove", "::std::wmemmove", "::wmemcpy", "::std::wmemcpy"};
}
}
} // namespace modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h
index 16510174c1ee552..f9cf38bad1d9181 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h
@@ -26,11 +26,6 @@ class ReplaceWithStdCopyCheck : public ClangTidyCheck {
MemmoveAndMemcpy,
};
- enum class StdCallsReplacementStyle {
- FreeFunc,
- MemberCall,
- };
-
ReplaceWithStdCopyCheck(StringRef Name, ClangTidyContext *Context);
~ReplaceWithStdCopyCheck() override = default;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
@@ -70,11 +65,7 @@ class ReplaceWithStdCopyCheck : public ClangTidyCheck {
// with their members or free fns.
static constexpr auto FlaggableCalleesDefault = FlaggableCallees::OnlyMemmove;
- static constexpr auto StdCallsReplacementStyleDefault =
- StdCallsReplacementStyle::FreeFunc;
-
const FlaggableCallees FlaggableCallees_;
- const StdCallsReplacementStyle StdCallsReplacementStyle_;
};
} // namespace clang::tidy::modernize
>From 5ec498fc945045d8c2363e0e3e03a0bb90e5c350 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Sun, 5 Jan 2025 18:17:10 +0100
Subject: [PATCH 05/15] Version passing the reduced test suite [skip ci]
---
.../modernize/ModernizeTidyModule.cpp | 2 +-
.../modernize/ReplaceWithStdCopyCheck.cpp | 417 ++++++++++--------
.../modernize/ReplaceWithStdCopyCheck.h | 26 +-
.../modernize/replace-with-std-copy-small.cpp | 150 +++++++
.../modernize/replace-with-std-copy.cpp | 3 +-
5 files changed, 391 insertions(+), 207 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-small.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 0c2c351cc71eb40..c1c3f8f0f5a72d3 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -96,7 +96,7 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<ReplaceDisallowCopyAndAssignMacroCheck>(
"modernize-replace-disallow-copy-and-assign-macro");
CheckFactories.registerCheck<ReplaceWithStdCopyCheck>(
- "modernize-replace-with-stdcopy");
+ "modernize-replace-with-std-copy");
CheckFactories.registerCheck<ReplaceRandomShuffleCheck>(
"modernize-replace-random-shuffle");
CheckFactories.registerCheck<ReturnBracedInitListCheck>(
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
index b594018675989fc..54f5a29947ddade 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
@@ -23,30 +23,10 @@ using namespace clang::ast_matchers;
namespace clang::tidy {
-template <>
-struct OptionEnumMapping<
- enum modernize::ReplaceWithStdCopyCheck::FlaggableCallees> {
- static llvm::ArrayRef<std::pair<
- modernize::ReplaceWithStdCopyCheck::FlaggableCallees, StringRef>>
- getEnumMapping() {
- static constexpr std::pair<
- modernize::ReplaceWithStdCopyCheck::FlaggableCallees, StringRef>
- Mapping[] = {
- {modernize::ReplaceWithStdCopyCheck::FlaggableCallees::
- MemmoveAndMemcpy,
- "MemmoveAndMemcpy"},
- {modernize::ReplaceWithStdCopyCheck::FlaggableCallees::OnlyMemmove,
- "OnlyMemmove"},
- };
- return {Mapping};
- }
-};
-
namespace modernize {
namespace {
constexpr llvm::StringLiteral ExpressionRef = "::std::memcpy";
-constexpr llvm::StringLiteral StdCopyHeader = "<algorithm>";
constexpr llvm::StringLiteral ReturnValueDiscardedRef =
"ReturnValueDiscardedRef";
@@ -57,8 +37,11 @@ struct Refs {
llvm::StringLiteral AsCArray;
size_t FallbackParameterIdx;
llvm::StringLiteral ValueType;
+ llvm::StringLiteral PtrCastFnReturnType;
+ llvm::StringLiteral AsSmartPtr;
};
-
+// rn matchers have a problem with binding types correctly, clang-query build up
+// the matchers from this file tmrw for debugging
template <typename RefsT> auto createPtrArgMatcher() {
constexpr Refs Refs = RefsT::Refs;
@@ -72,31 +55,38 @@ template <typename RefsT> auto createPtrArgMatcher() {
"::std::basic_string_view", "::std::span");
}();
- auto ValueTypeM = type().bind(Refs.ValueType);
-
auto AllowedContainerTypeM = hasUnqualifiedDesugaredType(
recordType(hasDeclaration(recordDecl(classTemplateSpecializationDecl(
AllowedContainerNamesM,
- hasTemplateArgument(
- 0, templateArgument(refersToType(
- hasUnqualifiedDesugaredType(ValueTypeM)))))))));
+ hasTemplateArgument(0, templateArgument(refersToType(
+ qualType().bind(Refs.ValueType)))))))));
+
+ auto CArrayTypeM = arrayType(hasElementType(qualType().bind(Refs.ValueType)));
auto VariantContainerM =
expr(hasType(AllowedContainerTypeM)).bind(Refs.AsContainer);
- auto VariantCArrayM =
- expr(hasType(arrayType(hasElementType(ValueTypeM)))).bind(Refs.AsCArray);
- auto StdDataReturnM = returns(pointerType(pointee(
- hasUnqualifiedDesugaredType(equalsBoundNode(Refs.ValueType.str())))));
+ auto VariantCArrayM = expr(hasType(CArrayTypeM)).bind(Refs.AsCArray);
+
+ auto VariantSmartPtrM =
+ expr(hasType(pointerType(pointee(recordType(hasDeclaration(cxxRecordDecl(
+ isSameOrDerivedFrom(classTemplateSpecializationDecl(
+ hasAnyName("::std::unique_ptr", "::std::shared_ptr"),
+ hasTemplateArgument(0, refersToType(CArrayTypeM)))))))))))
+ .bind(Refs.AsSmartPtr);
+ auto VariantRawPtrM =
+ expr(hasType(pointerType(pointee(qualType().bind(Refs.ValueType)))));
+
+ auto StdDataReturnM =
+ returns(pointerType(pointee(qualType().bind(Refs.PtrCastFnReturnType))));
auto StdDataMemberDeclM =
cxxMethodDecl(hasName("data"), parameterCountIs(0), StdDataReturnM);
// ,unless(isConst()) // __jm__ ONLY difference between Dest
// and Source calls, but maybe don't include it?
- auto StdDataFreeDeclM = functionDecl(
- hasName("::std::data"), parameterCountIs(1),
- StdDataReturnM); // __jm__ possibly elaborate on argument type here?
+ auto StdDataFreeDeclM = functionDecl(hasAnyName("::std::data", "::data"),
+ parameterCountIs(1), StdDataReturnM);
auto StdDataMemberCallM = cxxMemberCallExpr(
callee(StdDataMemberDeclM), argumentCountIs(0),
@@ -109,46 +99,62 @@ template <typename RefsT> auto createPtrArgMatcher() {
// the last expr() in anyOf assumes previous matchers are ran eagerly from
// left to right, still need to test this is the actual behaviour
- return expr(
- anyOf(StdDataMemberCallM, StdDataFreeCallM, VariantCArrayM, expr()));
+ return expr(anyOf(StdDataMemberCallM, StdDataFreeCallM, VariantCArrayM,
+ VariantSmartPtrM, VariantRawPtrM));
}
namespace tag {
+struct RawPtr {};
struct Container {};
struct CArray {};
-struct RawPtr {};
+struct SmartPtr {};
} // namespace tag
struct PtrArg {
- std::variant<tag::Container, tag::CArray, tag::RawPtr> Tag;
- const Expr *Node;
+ std::variant<tag::RawPtr, tag::Container, tag::CArray, tag::SmartPtr> Tag;
+ const Expr &Node;
};
template <typename RefT>
-auto extractNode(const CallExpr &CallNode,
- const MatchFinder::MatchResult &Result) -> PtrArg {
+PtrArg extractNode(const CallExpr &CallNode,
+ const MatchFinder::MatchResult &Result) {
constexpr Refs Refs = RefT::Refs;
- if (const auto *Node = Result.Nodes.getNodeAs<Expr>(Refs.AsCArray))
- return {tag::CArray{}, Node};
- if (const auto *Node = Result.Nodes.getNodeAs<Expr>(Refs.AsContainer))
- return {tag::Container{}, Node};
- return {tag::RawPtr{}, CallNode.getArg(Refs.FallbackParameterIdx)};
+ if (const auto *Node = Result.Nodes.getNodeAs<Expr>(Refs.AsCArray);
+ Node != nullptr)
+ return {tag::CArray{}, *Node};
+ if (const auto *Node = Result.Nodes.getNodeAs<Expr>(Refs.AsContainer);
+ Node != nullptr)
+ return {tag::Container{}, *Node};
+ if (const auto *Node = Result.Nodes.getNodeAs<Expr>(Refs.AsSmartPtr);
+ Node != nullptr)
+ return {tag::SmartPtr{}, *Node};
+ return {tag::RawPtr{}, *CallNode.getArg(Refs.FallbackParameterIdx)};
}
template <typename RefT>
-QualType extractValueType(const MatchFinder::MatchResult &Result) {
- return *Result.Nodes.getNodeAs<QualType>(RefT::Refs.ValueType);
+const QualType *extractValueType(const MatchFinder::MatchResult &Result) {
+ constexpr Refs Refs = RefT::Refs;
+ const auto *MaybeRetType =
+ Result.Nodes.getNodeAs<QualType>(Refs.PtrCastFnReturnType);
+ const auto *ValueType = Result.Nodes.getNodeAs<QualType>(Refs.ValueType);
+ llvm::errs() << "MaybeRetType: " << (MaybeRetType == nullptr) << '\n';
+ llvm::errs() << "ValueType: " << (ValueType == nullptr) << '\n';
+
+ // checking equality is done here as opposed to when matching because the
+ // equalsBoundNode matcher depends on the match order and the
+ // PtrCastFnReturnType is only present in some scenarios
+ if (MaybeRetType != nullptr and
+ MaybeRetType->getCanonicalType() != ValueType->getCanonicalType())
+ return nullptr;
+ return ValueType;
}
} // namespace ptrarg
namespace dst {
-constexpr size_t argIndex = 0;
+constexpr size_t ArgIndex = 0;
struct RefT {
static constexpr ptrarg::Refs Refs = {
- "Dst::AsContainer",
- "Dst::AsCArray",
- 0,
- "Dst::ValueType",
- };
+ "Dst::AsContainer", "Dst::AsCArray", 0, "Dst::ValueType",
+ "Dst::PtrCastFnReturnType", "Dst::AsSmartPtr"};
};
auto createMatcher() { return ptrarg::createPtrArgMatcher<RefT>(); }
@@ -162,15 +168,12 @@ auto extractValueType(const MatchFinder::MatchResult &Result) {
} // namespace dst
namespace src {
-constexpr size_t argIndex = 1;
+constexpr size_t ArgIndex = 1;
struct SrcRefsT {
static constexpr ptrarg::Refs Refs = {
- "Src::AsContainer",
- "Src::AsCArray",
- 1,
- "Src::ValueType",
- };
+ "Src::AsContainer", "Src::AsCArray", 1, "Src::ValueType",
+ "Src::PtrCastFnReturnType", "Src::AsSmartPtr"};
};
auto createMatcher() { return ptrarg::createPtrArgMatcher<SrcRefsT>(); }
@@ -184,7 +187,7 @@ auto extractValueType(const MatchFinder::MatchResult &Result) {
} // namespace src
namespace size {
-constexpr size_t argIndex = 2;
+constexpr size_t ArgIndex = 2;
// cases:
// 1. sizeof(T) where T is a type
// 2. sizeof(rec) where rec is a CArray
@@ -193,14 +196,14 @@ constexpr size_t argIndex = 2;
// 5. other expr
namespace variant {
struct SizeOfExpr {
- const Expr *Arg;
+ const Expr &Arg;
};
struct NSizeOfExpr {
- const Expr *N;
- const Expr *Arg;
+ const Expr &N;
+ const Expr &Arg;
};
struct Strlen {
- const Expr *Arg;
+ const Expr &Arg;
};
} // namespace variant
using SizeArg = std::variant<variant::SizeOfExpr, variant::NSizeOfExpr,
@@ -227,36 +230,41 @@ static constexpr struct Refs {
};
auto createMatcher() {
- auto NSizeOfT =
+ auto NSizeOfExprM =
binaryOperator(hasOperatorName("*"),
hasOperands(expr().bind(Refs.N),
sizeOfExpr(has(expr().bind(Refs.SizeOfArg)))));
- auto Strlen = callExpr(callee(functionDecl(hasAnyName(
- "::strlen", "::std::strlen", "::wcslen",
- "::std::wcslen", "::strnlen_s", "::std::strnlen_s",
- "::wcsnlen_s", "::std::wcsnlen_s"))),
- hasArgument(0, expr().bind(Refs.StrlenArg)));
- auto StrlenPlusOne = binaryOperator(
- hasOperatorName("+"), hasOperands(Strlen, integerLiteral(equals(1))));
-
- auto SizeOfExpr = sizeOfExpr(hasArgumentOfType(type().bind(Refs.SizeOfArg)));
-
- return expr(anyOf(NSizeOfT, Strlen, StrlenPlusOne, SizeOfExpr));
+ auto StrlenM =
+ callExpr(callee(functionDecl(hasAnyName(
+ "::strlen", "::std::strlen", "::wcslen", "::std::wcslen",
+ "::strnlen_s", "::std::strnlen_s", "::wcsnlen_s",
+ "::std::wcsnlen_s"))),
+ hasArgument(0, expr().bind(Refs.StrlenArg)));
+ auto StrlenPlusOneM = binaryOperator(
+ hasOperatorName("+"), hasOperands(StrlenM, integerLiteral(equals(1))));
+
+ auto SizeOfExprM = sizeOfExpr(has(expr().bind(Refs.SizeOfArg)));
+
+ return expr(anyOf(NSizeOfExprM, StrlenM, StrlenPlusOneM, SizeOfExprM));
}
SizeArg extractNode(const CallExpr &CallNode,
const MatchFinder::MatchResult &Result) {
+ llvm::errs() << __LINE__ << '\n';
if (const auto *SizeOfArgNode = Result.Nodes.getNodeAs<Expr>(Refs.SizeOfArg);
SizeOfArgNode != nullptr) {
+ llvm::errs() << __LINE__ << '\n';
if (const auto *NNode = Result.Nodes.getNodeAs<Expr>(Refs.N);
NNode != nullptr)
- return variant::NSizeOfExpr{NNode, SizeOfArgNode};
- return variant::SizeOfExpr{SizeOfArgNode};
+ return variant::NSizeOfExpr{*NNode, *SizeOfArgNode};
+ llvm::errs() << __LINE__ << '\n';
+ return variant::SizeOfExpr{*SizeOfArgNode};
}
+ llvm::errs() << __LINE__ << '\n';
if (const auto *StrlenArgNode = Result.Nodes.getNodeAs<Expr>(Refs.StrlenArg);
- StrlenArgNode != nullptr) {
- return variant::Strlen{StrlenArgNode};
- }
+ StrlenArgNode != nullptr)
+ return variant::Strlen{*StrlenArgNode};
+ llvm::errs() << __LINE__ << '\n';
return CallNode.getArg(2);
}
// 1. N * sizeof(type) (commutative) - issue warning but no fixit, or fixit
@@ -268,6 +276,15 @@ SizeArg extractNode(const CallExpr &CallNode,
} // namespace size
+auto createCalleeMatcher(bool FlagMemcpy) {
+ if (FlagMemcpy)
+ return hasAnyName("::memmove", "::std::memmove", "::memcpy",
+ "::std::memcpy", "::wmemmove", "::std::wmemmove",
+ "::wmemcpy", "::std::wmemcpy");
+ return hasAnyName("::memmove", "::std::memmove", "::wmemmove",
+ "::std::wmemmove");
+}
+
} // namespace
ReplaceWithStdCopyCheck::ReplaceWithStdCopyCheck(StringRef Name,
@@ -276,15 +293,14 @@ ReplaceWithStdCopyCheck::ReplaceWithStdCopyCheck(StringRef Name,
Inserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM),
areDiagsSelfContained()),
- FlaggableCallees_(Options.getLocalOrGlobal("FlaggableCallees",
- FlaggableCalleesDefault)) {}
+ FlagMemcpy(Options.getLocalOrGlobal("FlagMemcpy", false)) {}
void ReplaceWithStdCopyCheck::registerMatchers(MatchFinder *Finder) {
const auto ReturnValueUsedM =
hasParent(compoundStmt().bind(ReturnValueDiscardedRef));
const auto OffendingDeclM =
- functionDecl(parameterCountIs(3), hasAnyName(getFlaggableCallees()));
+ functionDecl(parameterCountIs(3), createCalleeMatcher(FlagMemcpy));
// cases:
// 1. One of the arguments is definitely a collection and the other a pointer
@@ -292,13 +308,12 @@ void ReplaceWithStdCopyCheck::registerMatchers(MatchFinder *Finder) {
// 2. Both source and dest are pointers, but size is of the form ((N :=
// expr()) * sizeof(bytelike())) - match (false positive if N \in {0, 1})
- const auto Expression =
- callExpr(callee(OffendingDeclM),
- anyOf(optionally(ReturnValueUsedM),
- allOf(hasArgument(0, dst::createMatcher()),
- hasArgument(1, src::createMatcher()),
- hasArgument(2, size::createMatcher()))));
- Finder->addMatcher(Expression.bind(ExpressionRef), this);
+ const auto ExpressionM = callExpr(
+ callee(OffendingDeclM), optionally(ReturnValueUsedM),
+ allOf(optionally(hasArgument(dst::ArgIndex, dst::createMatcher())),
+ optionally(hasArgument(src::ArgIndex, src::createMatcher())),
+ optionally(hasArgument(size::ArgIndex, size::createMatcher()))));
+ Finder->addMatcher(ExpressionM.bind(ExpressionRef), this);
}
void ReplaceWithStdCopyCheck::registerPPCallbacks(
@@ -308,132 +323,186 @@ void ReplaceWithStdCopyCheck::registerPPCallbacks(
void ReplaceWithStdCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
- Options.store(Opts, "FlaggableCallees", FlaggableCallees_);
+ Options.store(Opts, "FlagMemcpy", FlagMemcpy);
}
-#include "llvm/Support/Debug.h"
-#define DEBUG_TYPE "clang-tidy"
-
void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
const auto &CallNode = *Result.Nodes.getNodeAs<CallExpr>(ExpressionRef);
-
- auto dstVT = dst::extractValueType(Result);
- auto srcVT = src::extractValueType(Result);
-
- // basis for converting size argument to std::copy_n's when issuing fixit
- auto valueTypeWidth = Result.Context->getTypeSizeInChars(dstVT);
-
- // Don't report cases where value type widths differ, as this might indicate
- // [de]serialization and hard to reason about replacements using std::copy[_n]
- if (valueTypeWidth != Result.Context->getTypeSizeInChars(srcVT))
+ llvm::errs() << __LINE__ << ' '
+ << Lexer::getSourceText(
+ CharSourceRange::getTokenRange(CallNode.getSourceRange()),
+ *Result.SourceManager, getLangOpts())
+ << '\n';
+
+ auto Dst = dst::extractNode(CallNode, Result);
+ auto Src = src::extractNode(CallNode, Result);
+ auto Size = size::extractNode(CallNode, Result);
+
+ const auto *DstVT = dst::extractValueType(Result);
+ if (DstVT == nullptr)
+ return;
+ const auto *SrcVT = src::extractValueType(Result);
+ if (SrcVT == nullptr)
return;
- auto dst = dst::extractNode(CallNode, Result);
- auto src = src::extractNode(CallNode, Result);
- auto size = size::extractNode(CallNode, Result);
-
- // only have this function return a non-empty optional if the form of the size
- // argument strongly indicates collection-related usage
- auto exprAsString = [&](const Expr *Node) {
+ auto ExprAsString = [&](const Expr &Node) {
return Lexer::getSourceText(
- CharSourceRange::getTokenRange(Node->getSourceRange()),
+ CharSourceRange::getTokenRange(Node.getSourceRange()),
*Result.SourceManager, getLangOpts())
.str();
};
- auto extractComponents = [&]() -> std::optional<size::SizeComponents> {
+ // only have this function return a non-empty optional if the form of the size
+ // argument strongly indicates collection-related usage
+ auto ExtractComponents = [&]() -> std::optional<size::SizeComponents> {
if (const auto *NSizeOfExprNode =
- std::get_if<size::variant::NSizeOfExpr>(&size);
+ std::get_if<size::variant::NSizeOfExpr>(&Size);
NSizeOfExprNode != nullptr) {
auto &[N, Arg] = *NSizeOfExprNode;
- auto sizeOfArgWidth = Result.Context->getTypeSizeInChars(Arg->getType());
- return {{sizeOfArgWidth, exprAsString(N)}};
+ auto SizeOfArgWidth = Result.Context->getTypeSizeInChars(Arg.getType());
+ return {{SizeOfArgWidth, ExprAsString(N)}};
}
- if (const auto *StrlenNode = std::get_if<size::variant::Strlen>(&size);
+ if (const auto *StrlenNode = std::get_if<size::variant::Strlen>(&Size);
StrlenNode != nullptr) {
- auto strlenArgTypeWidth = Result.Context->getTypeSizeInChars(
- StrlenNode->Arg->getType()->getPointeeType());
- return {{strlenArgTypeWidth, exprAsString(CallNode.getArg(2))}};
+ auto StrlenArgTypeWidth = Result.Context->getTypeSizeInChars(
+ StrlenNode->Arg.getType()->getPointeeType());
+ return {{StrlenArgTypeWidth, ExprAsString(*CallNode.getArg(2))}};
}
return std::nullopt;
};
- if (auto maybeComponents = extractComponents(); maybeComponents.has_value()) {
- auto [unit, nExpr] = *maybeComponents;
- if (unit != valueTypeWidth) {
- // __jm__ tricky to offer a helpful fixit here, as the size argument
- // doesn't seem to be related to the pointee types of src and dst
- return;
- }
- // __jm__ makes sense to assume
- }
- // might be a reinterpretation call, only other case where we don't flag
- if (std::holds_alternative<ptrarg::tag::RawPtr>(dst.Tag) and
- std::holds_alternative<ptrarg::tag::RawPtr>(src.Tag)) {
- // not supported yet, need to come up with a robust heuristic first
+ bool DstIsRawPtr = std::holds_alternative<ptrarg::tag::RawPtr>(Dst.Tag);
+ bool SrcIsRawPtr = std::holds_alternative<ptrarg::tag::RawPtr>(Src.Tag);
+
+ auto CheckIsFalsePositive = [&]() {
+ // This case could be supported but it requires a robust heuristic
+ // over the form of the size argument
+ // to deduce we are dealing with pointers to collections
+ // When only one of the arguments is a raw pointer, then there are still
+ // valid scenarios in which it is a singleton to be memmoved over, like
+ // [de]serialization.
+ return DstIsRawPtr or SrcIsRawPtr;
+ };
+ if (CheckIsFalsePositive())
return;
- }
- // both src and dst are collection expressions, enough to wrap them in
- // std::begin calls
auto Diag = diag(CallNode.getExprLoc(), "prefer std::copy_n to %0")
<< cast<NamedDecl>(CallNode.getCalleeDecl());
- // don't issue a fixit if the result of the call is used
- if (bool IsReturnValueUsed =
- Result.Nodes.getNodeAs<Stmt>(ReturnValueDiscardedRef) == nullptr;
- IsReturnValueUsed)
+ // basis for converting size argument to std::copy_n's when issuing fixit
+ auto DstTypeWidth = Result.Context->getTypeSizeInChars(*DstVT);
+ auto SrcTypeWidth = Result.Context->getTypeSizeInChars(*SrcVT);
+
+ auto CheckIsFixable = [&]() {
+ // If the width types differ, it's hard to reason about what would be a
+ // helpful replacement, so just don't issue a fixit in this case
+ if (DstTypeWidth != SrcTypeWidth)
+ return false;
+
+ // don't issue a fixit if the result of the call is used
+ if (bool IsReturnValueUsed =
+ Result.Nodes.getNodeAs<Stmt>(ReturnValueDiscardedRef) == nullptr;
+ IsReturnValueUsed)
+ return false;
+
+ return true;
+ };
+ if (not CheckIsFixable())
return;
- Diag << FixItHint::CreateRemoval(CallNode.getSourceRange());
- std::string srcFixit = "std::cbegin(" + exprAsString(src.Node) + ")";
- std::string dstFixit = "std::begin(" + exprAsString(dst.Node) + ")";
+ // From here we can assume dst and src have equal value type widths
+ const auto &ValueTypeWidth = DstTypeWidth;
+
+ auto SrcFixit = [&]() {
+ auto AsString = ExprAsString(Src.Node);
+ if (SrcIsRawPtr)
+ return AsString;
+ return "std::cbegin(" + AsString + ")";
+ }();
+
+ auto DstFixit = [&]() {
+ auto AsString = ExprAsString(Dst.Node);
+ if (DstIsRawPtr)
+ return AsString;
+ return "std::begin(" + AsString + ")";
+ }();
- auto calleeIsWideVariant =
+ llvm::errs() << "SrcFixit: " << SrcFixit << '\n'
+ << "DstFixit: " << DstFixit << '\n';
+ auto CalleeIsWideVariant =
CallNode.getDirectCallee()->getParamDecl(0)->getType()->isWideCharType();
- auto calleeUnit = [&]() {
- if (calleeIsWideVariant) {
+ auto CalleeUnit = [&]() {
+ if (CalleeIsWideVariant) {
return Result.Context->getTypeSizeInChars(
(Result.Context->getWideCharType()));
}
return CharUnits::One();
}();
- auto sizeFixit = [&]() -> std::string {
- if (valueTypeWidth == calleeUnit) {
- return exprAsString(CallNode.getArg(size::argIndex));
+ llvm::errs() << __LINE__ << Size.index() << '\n';
+
+ llvm::errs() << __LINE__ << " CalleeUnit: " << CalleeUnit.getQuantity()
+ << '\n';
+ auto SizeFixit = [&]() -> std::string {
+ if (ValueTypeWidth == CalleeUnit) {
+ // replace sizeof(CArray) with the more modern std::size(CArray)
+ if (const auto *SizeOfExprNode =
+ std::get_if<size::variant::SizeOfExpr>(&Size);
+ SizeOfExprNode != nullptr and
+ SizeOfExprNode->Arg.getType()->isArrayType()) {
+ return "std::size(" + ExprAsString(SizeOfExprNode->Arg) + ")";
+ }
+
+ return ExprAsString(*CallNode.getArg(size::ArgIndex));
}
// try to factor out the unit from the size expression
- if (auto maybeSizeComponents = extractComponents();
- maybeSizeComponents.has_value() and
- maybeSizeComponents->Unit == valueTypeWidth) {
- return maybeSizeComponents->NExpr;
+ if (auto MaybeSizeComponents = ExtractComponents();
+ MaybeSizeComponents.has_value() and
+ MaybeSizeComponents->Unit == ValueTypeWidth) {
+ return MaybeSizeComponents->NExpr;
}
- // last resort, divide by valueTypeWidth
- return exprAsString(CallNode.getArg(size::argIndex)) + " / " + "sizeof(" +
- dstVT.getAsString() + ")";
+ // last resort, divide by ValueTypeWidth
+ return ExprAsString(*CallNode.getArg(size::ArgIndex)) + " / " + "sizeof(" +
+ DstVT->getAsString() + ")";
}();
+ llvm::errs() << "SizeFixit: " << SizeFixit << '\n';
+
+ Diag << FixItHint::CreateRemoval(CallNode.getSourceRange())
+ << FixItHint::CreateInsertion(CallNode.getBeginLoc(),
+ "std::copy_n(" + SrcFixit + ", " +
+ SizeFixit + ", " + DstFixit + ");")
+ << Inserter.createIncludeInsertion(
+ Result.SourceManager->getFileID(CallNode.getBeginLoc()),
+ "<algorithm>");
+
+ // Modern containers will include a std::[c]begin declaration with their
+ // header, but not c-arrays (or smart pointers to c-arrays), so need to ensure
+ // they are included.
+ auto IsCArrayOrWrapper = [](const decltype(ptrarg::PtrArg::Tag) &Tag) {
+ return std::holds_alternative<ptrarg::tag::CArray>(Tag) or
+ std::holds_alternative<ptrarg::tag::SmartPtr>(Tag);
+ };
- Diag << FixItHint::CreateInsertion(CallNode.getEndLoc(),
- "std::copy_n(" + srcFixit + ", " +
- sizeFixit + ", " + dstFixit + ");");
+ if (IsCArrayOrWrapper(Dst.Tag) and IsCArrayOrWrapper(Src.Tag))
+ Diag << Inserter.createIncludeInsertion(
+ Result.SourceManager->getFileID(CallNode.getBeginLoc()), "<iterator>");
// if (const auto *NSizeofExprNode =
// std::get_if<size::variant::NSizeOfExpr>(&size);
// NSizeofExprNode != nullptr) {
// auto &[N, Arg] = *NSizeofExprNode;
- // auto sizeOfArgWidth = Result.Context->getTypeSizeInChars(Arg->getType());
+ // auto SizeOfArgWidth = Result.Context->getTypeSizeInChars(Arg->getType());
// issueFixitIfWidthsMatch(dst.Node, src.Node,
- // {sizeOfArgWidth, exprAsString(N)});
+ // {SizeOfArgWidth, ExprAsString(N)});
// return;
// }
// if (const auto *StrlenNode = std::get_if<size::variant::Strlen>(&size);
// StrlenNode != nullptr) {
- // auto strlenArgTypeWidth = Result.Context->getTypeSizeInChars(
+ // auto StrlenArgTypeWidth = Result.Context->getTypeSizeInChars(
// StrlenNode->Arg->getType()->getPointeeType());
// issueFixitIfWidthsMatch(
// dst.Node, src.Node,
- // {strlenArgTypeWidth, exprAsString(CallNode.getArg(2))});
+ // {StrlenArgTypeWidth, ExprAsString(CallNode.getArg(2))});
// return;
// }
// if (const auto *SizeOfExprNode =
@@ -443,15 +512,15 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
// if (SizeOfExprNode->Arg->getType()->isArrayType()) {
// issueFixitIfWidthsMatch(
// dst.Node, src.Node,
- // {CharUnits::One(), exprAsString(CallNode.getArg(2))});
+ // {CharUnits::One(), ExprAsString(CallNode.getArg(2))});
// return;
// }
// // __jm__ weird bc we assume dst and src are collections
// // if Arg turns out to have the same type as dst or src then just suggest
// // copy via the assignment operator
- // if (auto argType = Arg->getType(); argType == dstVT or argType == srcVT)
+ // if (auto argType = Arg->getType(); argType == DstVT or argType == SrcVT)
// {
- // issueFixit{valueTypeWidth, exprAsString(CallNode.getArg(2))};
+ // issueFixit{ValueTypeWidth, ExprAsString(CallNode.getArg(2))};
// return;
// }
// // __jm__ only flag this as suspicious with a further explanation in the
@@ -489,7 +558,7 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
// equal to vtw, essentialy fallthrough to 3
// 3. N * sizeof(expr) is okay when expr is a type with width ==
- // valueTypeWidth and N may be verbatim lifted into copy_n's third argument
+ // ValueTypeWidth and N may be verbatim lifted into copy_n's third argument
// to make sure we can issue a FixIt, need to be pretty sure we're dealing
// with a collection and it is a byte collection
@@ -538,8 +607,6 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
// } else {
// }
- Diag << Inserter.createIncludeInsertion(
- Result.SourceManager->getFileID(CallNode.getBeginLoc()), StdCopyHeader);
{
// using namespace std::literals;
// Diag << FixItHint::CreateReplacement(
@@ -562,17 +629,7 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
// ")")
// .str());
}
- // dest source size -> source size dest
}
-llvm::ArrayRef<StringRef> ReplaceWithStdCopyCheck::getFlaggableCallees() const {
- switch (FlaggableCallees_) {
- case FlaggableCallees::OnlyMemmove:
- return {"::memmove", "::std::memmove", "::wmemmove", "::std::wmemmove"};
- case FlaggableCallees::MemmoveAndMemcpy:
- return {"::memmove", "::std::memmove", "::memcpy", "::std::memcpy",
- "::wmemmove", "::std::wmemmove", "::wmemcpy", "::std::wmemcpy"};
- }
-}
} // namespace modernize
} // namespace clang::tidy
\ No newline at end of file
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h
index f9cf38bad1d9181..e3e317bf7fb5e1b 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.h
@@ -13,7 +13,6 @@
#include "../utils/IncludeInserter.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/Basic/LangOptions.h"
-#include "llvm/ADT/SmallVector.h"
namespace clang::tidy::modernize {
@@ -21,11 +20,6 @@ namespace clang::tidy::modernize {
// calls to std::copy or std::copy_n, depending on the context
class ReplaceWithStdCopyCheck : public ClangTidyCheck {
public:
- enum class FlaggableCallees {
- OnlyMemmove,
- MemmoveAndMemcpy,
- };
-
ReplaceWithStdCopyCheck(StringRef Name, ClangTidyContext *Context);
~ReplaceWithStdCopyCheck() override = default;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
@@ -41,31 +35,13 @@ class ReplaceWithStdCopyCheck : public ClangTidyCheck {
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:
void tryIssueFixIt(const ast_matchers::MatchFinder::MatchResult &Result,
const DiagnosticBuilder &Diag, const CallExpr &CallNode);
- llvm::ArrayRef<StringRef> getFlaggableCallees() const;
utils::IncludeInserter Inserter;
- // __jm__ TODO options:
- // 1. list of functions that can be replaced, memmove by default,
- // and memcpy as opt-in
- // 2. should calls to {begin,end,size} be replaced
- // with their members or free fns.
- static constexpr auto FlaggableCalleesDefault = FlaggableCallees::OnlyMemmove;
- const FlaggableCallees FlaggableCallees_;
+ const bool FlagMemcpy;
};
} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-small.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-small.cpp
new file mode 100644
index 000000000000000..cc3845dcce9cc29
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-small.cpp
@@ -0,0 +1,150 @@
+// RUN: %check_clang_tidy %s modernize-replace-with-std-copy %t
+
+//CHECK-FIXES: #include <algorithm>
+
+namespace {
+using size_t = decltype(sizeof(int));
+
+void *memcpy(void *__restrict dest, const void *__restrict src, size_t n) {
+ return nullptr;
+}
+void *memmove(void *__restrict dest, const void *__restrict src, size_t n) {
+ return nullptr;
+}
+} // namespace
+
+namespace std {
+using int64_t = long long ;
+using int32_t = int ;
+using int16_t = short ;
+using int8_t = char ;
+
+using char32 = int32_t;
+using char16 = int16_t;
+using char8 = int8_t;
+
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T, typename A>
+struct basic_string {
+ typedef basic_string<C, T, A> _Type;
+ basic_string();
+ basic_string(const C* p, const A& a = A());
+
+ const C* c_str() const;
+ const C* data() const;
+
+ _Type& append(const C* s);
+ _Type& append(const C* s, size_t n);
+ _Type& assign(const C* s);
+ _Type& assign(const C* s, size_t n);
+
+ int compare(const _Type&) const;
+ int compare(const C* s) const;
+ int compare(size_t pos, size_t len, const _Type&) const;
+ int compare(size_t pos, size_t len, const C* s) const;
+
+ size_t find(const _Type& str, size_t pos = 0) const;
+ size_t find(const C* s, size_t pos = 0) const;
+ size_t find(const C* s, size_t pos, size_t n) const;
+
+ _Type& insert(size_t pos, const _Type& str);
+ _Type& insert(size_t pos, const C* s);
+ _Type& insert(size_t pos, const C* s, size_t n);
+
+ _Type& operator+=(const _Type& str);
+ _Type& operator+=(const C* s);
+ _Type& operator=(const _Type& str);
+ _Type& operator=(const C* s);
+};
+
+using string = basic_string<char, std::char_traits<char>, std::allocator<char>>;
+using wstring = basic_string<wchar_t, std::char_traits<wchar_t>,std::allocator<wchar_t>>;
+using u16string = basic_string<char16, std::char_traits<char16>, std::allocator<char16>>;
+using u32string = basic_string<char32, std::char_traits<char32>, std::allocator<char32>>;
+
+
+
+void *memcpy(void *__restrict dest, const void *__restrict src, size_t n);
+void *memmove(void *__restrict dest, const void *__restrict src, size_t n);
+
+template <typename T> struct vector {
+ vector(size_t);
+
+ T *data();
+ const T *data() const;
+ size_t size() const;
+ void resize(size_t);
+ using value_type = T;
+};
+
+template<typename T>
+T* data(vector<T>&);
+
+template<typename T>
+T* data(T[]);
+
+template<typename T>
+const T* data(const vector<T>&);
+
+template<typename T>
+const T* data(const T[]);
+
+size_t size(void *);
+
+size_t strlen(const char *);
+} // namespace std
+
+
+namespace {
+void notSupportedEx() {
+ char Source[] = "once upon a daydream...";
+
+ auto *PrimitiveDest = new std::int8_t;
+ std::memmove(PrimitiveDest, Source, sizeof PrimitiveDest);
+
+ double D = 0.1;
+ std::int64_t N;
+ // don't warn on calls over non-sequences
+ std::memmove(&N, &D, sizeof D);
+}
+
+void noFixItEx() {
+ // value type widths are different and so a fix is not straightforward
+ int Source[5];
+ std::vector<char> Dest(20);
+
+ memmove(std::data(Dest), Source, 20);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+}
+
+void sequenceOfBytesEx() {
+ {
+ char Source[] = "once upon a daydream...";
+ char Dest[4];
+
+ std::memmove(Dest, Source, sizeof Dest);
+ // CHECK-MESSAGES: [[@LINE-1]]:9: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Source), std::size(Dest), std::begin(Dest));
+
+ memmove(std::data(Dest), std::data(Source), sizeof Dest);
+ // CHECK-MESSAGES: [[@LINE-1]]:9: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Source), std::size(Dest), std::begin(Dest));
+ }
+
+ {
+ char Source[] = "once upon a daydream...";
+ std::vector<char> Dest(4);
+
+ std::memmove(Dest.data(), Source, Dest.size());
+ // CHECK-MESSAGES: [[@LINE-1]]:9: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Source), Dest.size(), std::begin(Dest));
+
+ std::memmove(std::data(Dest), Source, Dest.size());
+ // CHECK-MESSAGES: [[@LINE-1]]:9: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Source), Dest.size(), std::begin(Dest));
+ }
+}
+} // namespace
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
index 3d0dc1805167688..72d57f7c140e907 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
@@ -1,4 +1,5 @@
-// RUN: %check_clang_tidy %s modernize-replace-with-std-copy %t
+// RUN: %check_clang_tidy %s modernize-replace-with-std-copy %t -- \
+// RUN: -config='{CheckOptions: {modernize-replace-with-std-copy.FlagMemcpy: true}}'
// possible call scenarios, infeasible to cover all
// replacement type:
>From 9d03f98621dea25ea018bbb18c9ab9ab2a737694 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Sun, 5 Jan 2025 18:17:34 +0100
Subject: [PATCH 06/15] Add check to clangd
---
clang-tools-extra/clangd/TidyFastChecks.inc | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang-tools-extra/clangd/TidyFastChecks.inc b/clang-tools-extra/clangd/TidyFastChecks.inc
index de1a025602fa9c2..a5d40a486976a3b 100644
--- a/clang-tools-extra/clangd/TidyFastChecks.inc
+++ b/clang-tools-extra/clangd/TidyFastChecks.inc
@@ -326,6 +326,7 @@ FAST(modernize-redundant-void-arg, 1.0)
FAST(modernize-replace-auto-ptr, 0.0)
FAST(modernize-replace-disallow-copy-and-assign-macro, -0.0)
FAST(modernize-replace-random-shuffle, 1.0)
+FAST(modernize-replace-with-std-copy, 1.0)
FAST(modernize-return-braced-init-list, 1.0)
FAST(modernize-shrink-to-fit, 1.0)
FAST(modernize-type-traits, 1.0)
>From 6c2d8619e3574c4b71c6c6bc431dbe3d5e3c37ad Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Sun, 5 Jan 2025 18:17:55 +0100
Subject: [PATCH 07/15] Rename check .rst file
---
...with-stdcopy.rst => replace-with-std-copy.rst} | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
rename clang-tools-extra/docs/clang-tidy/checks/modernize/{replace-memcpy-with-stdcopy.rst => replace-with-std-copy.rst} (63%)
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-with-std-copy.rst
similarity index 63%
rename from clang-tools-extra/docs/clang-tidy/checks/modernize/replace-memcpy-with-stdcopy.rst
rename to clang-tools-extra/docs/clang-tidy/checks/modernize/replace-with-std-copy.rst
index 119a3184208c2aa..7a28ab75ac2b926 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-memcpy-with-stdcopy.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-with-std-copy.rst
@@ -3,18 +3,19 @@
modernize-replace-with-stdcopy
===================================
-Replaces all occurrences of the C ``memcpy`` function with ``std::copy``
+Replaces all occurrences of the C ``memmove`` function and its wide-char variant with ``std::copy_n``.
+Replacement of ``memcpy`` is optionally also supported.
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
+ * \param dst Pointer to the destination array where the content is to be copied
+ * \param src Pointer to the source of data to be copied
+ * \param size Number of bytes to copy
*/
- memcpy(destination, source, num);
+ memcpy(dst, src, size);
becomes
@@ -25,7 +26,7 @@ becomes
* \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);
+ std::copy_n(std::cbegin(src), size, std::begin(dst));
Bytes to iterator conversion
----------------------------
@@ -36,7 +37,7 @@ In order to make the check working, it will convert the size parameter to an ite
Header inclusion
----------------
-``std::copy`` being provided by the ``algorithm`` header file, this check will include it if needed.
+``std::copy_n`` is provided by the ``algorithm`` header file, this check will include it if needed.
Options
-------
>From c6afed0e96e0c781239cfc19f1d33e46b77f9eb0 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Mon, 6 Jan 2025 23:46:44 +0100
Subject: [PATCH 08/15] Split test suite into multiple files
---
.../modernize/replace-with-std-copy-nofix.cpp | 85 ++++
.../modernize/replace-with-std-copy-small.cpp | 8 +-
.../modernize/replace-with-std-copy.cpp | 382 +++++++++---------
3 files changed, 285 insertions(+), 190 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-nofix.cpp
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-nofix.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-nofix.cpp
new file mode 100644
index 000000000000000..6cdba0d0b8f7489
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-nofix.cpp
@@ -0,0 +1,85 @@
+// RUN: %check_clang_tidy %s modernize-replace-with-std-copy %t -- \
+// RUN: -config='{CheckOptions: {modernize-replace-with-std-copy.FlagMemcpy: true}}' \
+// RUN: | FileCheck %s -implicit-check-not="{{FIX-IT}}"
+
+namespace std {
+using size_t = decltype(sizeof(int));
+
+void *memcpy(void *__restrict dest, const void *__restrict src, size_t n);
+void *memmove(void *__restrict dest, const void *__restrict src, size_t n);
+
+template <typename T> struct vector {
+ vector(size_t);
+
+ T *data();
+ const T *data() const;
+ size_t size() const;
+ using value_type = T;
+};
+
+template<typename Container>
+typename Container::value_type* data(Container& Arg) {
+ return Arg.data();
+}
+
+template<typename T>
+T* data(T Arg[]);
+
+template<typename Container>
+const typename Container::value_type* data(const Container& Arg) {
+ return Arg.data();
+}
+
+template<typename T>
+const T* data(const T[]);
+
+} // namespace std
+
+namespace {
+using size_t = std::size_t;
+
+void *memmove(void *__restrict dest, const void *__restrict src, size_t n) {
+ return nullptr;
+}
+} // namespace
+
+namespace {
+void noFixItEx() {
+ { // different value type widths
+ int Source[10];
+ std::vector<char> Dest(5);
+
+ memmove(Source, std::data(Dest), 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ std::memmove(Source, std::data(Dest), 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ memmove(std::data(Source), Dest.data(), 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ std::memmove(std::data(Source), Dest.data(), 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ memmove(std::data(Source), std::data(Dest), 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ std::memmove(std::data(Source), std::data(Dest), 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ }
+
+ { // return value used
+ int Source[10];
+ std::vector<char> Dest(5);
+
+ void* Vptr = nullptr;
+ if (memmove(Source, Dest.data(), 1) != nullptr) {
+ // CHECK-MESSAGES: [[@LINE-1]]:9: warning: prefer std::copy_n to 'memmove'
+ Vptr = memmove(Source, Dest.data(), 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: prefer std::copy_n to 'memmove'
+ }
+ Vptr = [&]() {
+ return memmove(Source, Dest.data(), 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: prefer std::copy_n to 'memmove'
+ }();
+
+ for (;Vptr != nullptr; Vptr = memmove(Source, Dest.data(), 1)) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:35: warning: prefer std::copy_n to 'memmove'
+ }
+}
+} // namespace
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-small.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-small.cpp
index cc3845dcce9cc29..469faef619619ee 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-small.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy-small.cpp
@@ -109,6 +109,12 @@ void notSupportedEx() {
std::int64_t N;
// don't warn on calls over non-sequences
std::memmove(&N, &D, sizeof D);
+
+ std::vector<char> Dest(4);
+
+ // don't warn on memcpy by default
+ memcpy(Dest.data(), Source, Dest.size());
+ std::memcpy(std::data(Dest), Source, Dest.size());
}
void noFixItEx() {
@@ -120,7 +126,7 @@ void noFixItEx() {
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
}
-void sequenceOfBytesEx() {
+void supportedEx() {
{
char Source[] = "once upon a daydream...";
char Dest[4];
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
index 72d57f7c140e907..8927e4b1da2e462 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
@@ -2,12 +2,10 @@
// RUN: -config='{CheckOptions: {modernize-replace-with-std-copy.FlagMemcpy: true}}'
// possible call scenarios, infeasible to cover all
-// replacement type:
-// [ ] members when possible
// [X] all free
// source:
// type:
-// [ ] T[]
+// [ ] c-array
// std::data:
// [ ] free
// [ ] N/A
@@ -49,22 +47,14 @@
// [ ] y
// [ ] n
-// CHECK-FIXES: #include <algorithm>
-
-namespace {
-
+namespace std {
using size_t = decltype(sizeof(int));
-namespace std {
using int64_t = long long ;
using int32_t = int ;
using int16_t = short ;
using int8_t = char ;
-using char32 = int32_t;
-using char16 = int16_t;
-using char8 = int8_t;
-
template <typename T>
class allocator {};
template <typename T>
@@ -75,38 +65,35 @@ struct basic_string {
basic_string();
basic_string(const C* p, const A& a = A());
- const C* c_str() const;
const C* data() const;
+ C* data();
+ size_t size() const;
+};
- _Type& append(const C* s);
- _Type& append(const C* s, size_t n);
- _Type& assign(const C* s);
- _Type& assign(const C* s, size_t n);
+using string = basic_string<char, std::char_traits<char>, std::allocator<char>>;
+using wstring = basic_string<wchar_t, std::char_traits<wchar_t>,std::allocator<wchar_t>>;
+using u16string = basic_string<char16_t, std::char_traits<char16_t>, std::allocator<char16_t>>;
+using u32string = basic_string<char32_t, std::char_traits<char32_t>, std::allocator<char32_t>>;
- int compare(const _Type&) const;
- int compare(const C* s) const;
- int compare(size_t pos, size_t len, const _Type&) const;
- int compare(size_t pos, size_t len, const C* s) const;
+template <typename CharT>
+class basic_string_view {
+public:
+ basic_string_view();
- size_t find(const _Type& str, size_t pos = 0) const;
- size_t find(const C* s, size_t pos = 0) const;
- size_t find(const C* s, size_t pos, size_t n) const;
+ basic_string_view(const CharT *);
- _Type& insert(size_t pos, const _Type& str);
- _Type& insert(size_t pos, const C* s);
- _Type& insert(size_t pos, const C* s, size_t n);
+ basic_string_view(const CharT *, size_t);
- _Type& operator+=(const _Type& str);
- _Type& operator+=(const C* s);
- _Type& operator=(const _Type& str);
- _Type& operator=(const C* s);
-};
+ basic_string_view(const basic_string_view &);
-using string = basic_string<char, std::char_traits<char>, std::allocator<char>>;
-using wstring = basic_string<wchar_t, std::char_traits<wchar_t>,std::allocator<wchar_t>>;
-using u16string = basic_string<char16, std::char_traits<char16>, std::allocator<char16>>;
-using u32string = basic_string<char32, std::char_traits<char32>, std::allocator<char32>>;
+ basic_string_view &operator=(const basic_string_view &);
+
+ const CharT* data() const;
+
+ size_t size() const;
+};
+using string_view = basic_string_view<char>;
void *memcpy(void *__restrict dest, const void *__restrict src, size_t n);
@@ -122,200 +109,217 @@ template <typename T> struct vector {
using value_type = T;
};
-template<typename T>
-T* data(vector<T>&);
+template <typename T, size_t N>
+struct array {
+ T* begin();
+ T* end();
+ const T* begin() const;
+ const T* end() const;
+
+ size_t size() const;
+
+ const T* data() const;
+ T* data();
+
+ T _data[N];
+};
template<typename T>
-T* data(T[]);
+struct span {
+ template<size_t N>
+ span(T (&arr)[N]);
+
+ const T* data() const;
+ T* data();
+
+ using value_type = T;
+};
+
+template<typename Container>
+typename Container::value_type* data(Container& Arg) {
+ return Arg.data();
+}
template<typename T>
-const T* data(const vector<T>&);
+T* data(T Arg[]);
+
+template<typename Container>
+const typename Container::value_type* data(const Container& Arg) {
+ return Arg.data();
+}
template<typename T>
const T* data(const T[]);
size_t size(void *);
+template<typename Container>
+size_t size(const Container& c) {
+ return c.size();
+}
+
size_t strlen(const char *);
+
+wchar_t* wmemcpy( wchar_t* dest, const wchar_t* src, size_t count );
+wchar_t* wmemmove( wchar_t* dest, const wchar_t* src, size_t count );
+
+template <typename T>
+struct unique_ptr {
+ unique_ptr(T);
+ T *get() const;
+ explicit operator bool() const;
+ void reset(T *ptr);
+ T &operator*() const;
+ T *operator->() const;
+ T& operator[](size_t i) const;
+};
} // namespace std
-void *memcpy(void *__restrict dest, const void *__restrict src, size_t n);
-void *memmove(void *__restrict dest, const void *__restrict src, size_t n);
-} // namespace
+namespace {
+using size_t = std::size_t;
+void *memcpy(void *__restrict dest, const void *__restrict src, size_t n) {
+ return nullptr;
+}
+void *memmove(void *__restrict dest, const void *__restrict src, size_t n) {
+ return nullptr;
+}
+wchar_t* wmemcpy( wchar_t* dest, const wchar_t* src, size_t count ) {
+ return nullptr;
+}
+wchar_t* wmemmove( wchar_t* dest, const wchar_t* src, size_t count ) {
+ return nullptr;
+}
+} // namespace
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 StructType {
- int X{42};
- void *operator new(size_t, void *) noexcept { return nullptr; }
- } Struct;
- alignas(StructType) char Buf[sizeof(StructType)];
- StructType *Ps = new (Buf) StructType; // placement new
- // // don't warn on calls over non-sequences
- std::memcpy(Ps, &Struct, sizeof Struct);
-
- const char *PtrSource = "once upon a daydream...";
- char *PtrDest = new char[4];
- std::memcpy(Dest, PtrSource, sizeof Dest);
- std::memcpy(PtrDest, Source, 4);
-}
-void noFixItEx() {
- {
- int Source[10];
- std::vector<char> Dest(5);
-
- memmove(Source, std::data(Dest), 1);
- // CHECK-MESSAGES: [[@LINE-1]]:20: warning: prefer std::copy_n to memcpy
- std::memmove(Source, std::data(Dest), 1);
- memmove(std::data(Source), std::data(Dest), 1);
- std::memmove(std::data(Source), std::data(Dest), 1);
- memmove(std::data(Source), std::data(Dest), 1);
- std::memmove(std::data(Source), std::data(Dest), 1);
+ { // pointee is not a collection
+ char Source[] = "once upon a daydream...";
+ auto *PrimitiveDest = new std::int8_t;
+
+ std::memcpy(PrimitiveDest, Source, sizeof PrimitiveDest);
}
- char Source[] = "once upon a daydream...", Dest[4];
- // no FixIt when return value is used
+ { // reinterpretation
+ double D = 0.1;
+ std::int64_t N;
+ // don't warn on calls over non-sequences
+ std::memcpy(&N, &D, sizeof D);
+ }
+
+ { // [de]serialization
+ struct ComplexObj {
+ int A;
+ float B;
+ };
+ auto *Obj = new ComplexObj();
+
+ char Src[sizeof(ComplexObj)] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+ std::memcpy(Obj, Src, sizeof(ComplexObj));
+ char Dst[sizeof(ComplexObj)];
+ std::memcpy(Dst, Obj, sizeof(ComplexObj));
+ }
+
+ { // incomplete array type (should be treated the same as a raw ptr)
+ char Src[] = "once upon a daydream...";
+ auto CopySrc = [&Src](char Dst[], size_t sz) {
+ std::memcpy(Dst, Src, sz);
+ };
- [](auto){}(std::memcpy(Dest, Source, sizeof Dest));
- // CHECK-MESSAGES: [[@LINE-1]]:20: warning: prefer std::copy_n to memcpy
+ char Dst[4];
+ CopySrc(Dst, 4);
+ }
- std::vector<std::int16_t> VecI16(4);
- // not a supported type, should be a sequence of bytes, otherwise it is difficult to compute the n in copy_n
- std::memcpy(VecI16.data(), Source,
- VecI16.size() * sizeof(decltype(VecI16)::value_type));
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ { // pointers
+ const char *Src = "once upon a daydream...";
+ char *Dst = new char[64];
- std::memcpy(std::data(VecI16), Source,
- VecI16.size() * sizeof(decltype(VecI16)::value_type));
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
+ std::memcpy(Dst, Src, std::strlen(Src));
+ }
}
-void sequenceOfBytesEx() {
- // the check should support memcpy conversion for the following types:
- // T[]
- // std::vector<T>
- // std::span<T>
- // std::deque<T>
- // std::array<T, _>
- // std::string
- // std::string_view
- // where T is byte-like
-
- {
- char Source[] = "once upon a daydream...";
- char Dest[4];
+void supportedEx() {
+ { // two wchar c-arrays
+ wchar_t Src[] = L"once upon a daydream...";
+ wchar_t Dst[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));
+ std::wmemcpy(Dst, Src, sizeof Dst);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Dst), std::begin(Dst));
- std::memcpy(std::data(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));
+ std::wmemcpy(Dst, Src, sizeof(Dst));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Dst), std::begin(Dst));
- std::memcpy(Dest, std::data(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));
+ wmemcpy(Dst, Src, sizeof Src);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Src), std::begin(Dst));
- std::memcpy(std::data(Dest), std::data(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));
+ wmemmove(Dst, Src, sizeof(Src));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Src), std::begin(Dst));
+ }
- // __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::string + std::vector
+ std::string Src = "once upon a daydream...";
+ std::vector<char> Dst(4);
- memcpy(std::data(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::memcpy(Dst.data(), Src.data(), Dst.size());
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), Dst.size(), std::begin(Dst));
+ }
- memcpy(Dest, std::data(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::string + std::vector
+ std::string Src = "once upon a daydream...";
+ std::vector<char> Dst(4);
- memcpy(std::data(Dest), std::data(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::memmove(Dst.data(), Src.data(), Src.size());
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), Src.size(), std::begin(Dst));
}
- {
- char Source[] = "once upon a daydream...";
- std::vector<char> Dest(4);
+ { // std::wstring + std::unique_ptr
+ std::wstring Src = L"once upon a daydream...";
+ std::unique_ptr<wchar_t[16]> Dst(new wchar_t[16]);
+
+ std::wmemcpy(*Dst, Src.data(), sizeof(*Dst));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(*Dst), std::begin(*Dst));
+ }
- std::memcpy(Dest.data(), Source, Dest.size());
- // 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));
+ { // std::string_view + std::array
+ std::string_view Src = "once upon a daydream...";
+ std::array<char, 16> Dst;
- std::memcpy(std::data(Dest), Source, Dest.size());
- // 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));
+ std::memmove(Dst.data(), Src.data(), Dst.size() - 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), Dst.size() - 1, std::begin(Dst));
}
- {
- std::vector<char> Source(10);
- char Dest[4];
-
- std::memcpy(Dest, Source.data(), Source.size());
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
- // CHECK-FIXES: std::copy_n(std::begin(Source), std::size(Source), std::begin(Dest));
- std::memcpy(Dest, Source.data(), sizeof(Dest));
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
- // CHECK-FIXES: std::copy_n(Source.data(), std::size(Dest), std::begin(Dest));
- std::memcpy(Dest, Source.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(Source), std::begin(Source));
-
- std::memcpy(Dest, Source.data(), 1 + Source.size() / 2);
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
- // CHECK-FIXES: std::copy_n(std::begin(Source), 1 + std::size(Source) / 2, std::begin(Dest));
- std::memcpy(Dest, Source.data(), 1 + sizeof(Dest) / 2);
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
- // CHECK-FIXES: std::copy_n(Source.data(), 1 + std::size(Dest) / 2, std::begin(Dest));
- std::memcpy(Dest, Source.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(Source));
-
- std::memcpy(Dest, std::data(Source), Source.size());
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
- // CHECK-FIXES: std::copy_n(std::begin(Source), std::size(Source), std::begin(Dest));
- std::memcpy(Dest, std::data(Source), sizeof(Dest));
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
- // CHECK-FIXES: std::copy_n(Source.data(), std::size(Dest), std::begin(Dest));
- std::memcpy(Dest, std::data(Source), 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(Source), std::begin(Source));
-
- std::memcpy(Dest, std::data(Source), 1 + Source.size() / 2);
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
- // CHECK-FIXES: std::copy_n(std::begin(Source), 1 + std::size(Source) / 2, std::begin(Dest));
- std::memcpy(Dest, std::data(Source), 1 + sizeof(Dest) / 2);
- // CHECK-MESSAGES: [[@LINE-1]]:8: warning: prefer std::copy_n to memcpy
- // CHECK-FIXES: std::copy_n(Source.data(), 1 + std::size(Dest) / 2, std::begin(Dest));
- std::memcpy(Dest, std::data(Source), 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(Source));
+ { // using namespace std;
+ using namespace std;
+ string_view Src = "once upon a daydream...";
+ array<char, 16> Dst;
+
+ memmove(Dst.data(), Src.data(), Src.size() - 2);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), Src.size() - 2, std::begin(Dst));
}
- // __jm__ make configurable whether stl containers should use members or free fns.
- // __jm__ for now use free fns. only
+ { // NSizeOfExpr cases
+ std::int32_t Data[] = {1, 2, 3, 4, 1, 2, 3, 4};
+ std::span<std::int32_t> Src{Data};
+ std::vector<std::int32_t> Dst(8);
+
+ memcpy(Dst.data(), Src.data(), Dst.size() * sizeof(std::int32_t));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), Dst.size(), std::begin(Dst));
+ memmove(std::data(Dst), std::data(Src), sizeof(int) * std::size(Dst));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Dst), std::begin(Dst));
+ }
}
} // namespace
\ No newline at end of file
>From 7467c7226f95a042d0da208cf0a95dbb6e9aaa2d Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Mon, 6 Jan 2025 23:47:20 +0100
Subject: [PATCH 09/15] WIP size argument semantics
---
.../modernize/ReplaceWithStdCopyCheck.cpp | 211 ++++++++++--------
1 file changed, 117 insertions(+), 94 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
index 54f5a29947ddade..28d755e6767f373 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "ReplaceWithStdCopyCheck.h"
+#include "ReplaceAutoPtrCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
@@ -38,7 +39,6 @@ struct Refs {
size_t FallbackParameterIdx;
llvm::StringLiteral ValueType;
llvm::StringLiteral PtrCastFnReturnType;
- llvm::StringLiteral AsSmartPtr;
};
// rn matchers have a problem with binding types correctly, clang-query build up
// the matchers from this file tmrw for debugging
@@ -46,13 +46,12 @@ template <typename RefsT> auto createPtrArgMatcher() {
constexpr Refs Refs = RefsT::Refs;
auto AllowedContainerNamesM = []() {
- if constexpr (true)
- return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
- "::std::vector", "::std::basic_string");
- else
- return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
- "::std::vector", "::std::basic_string",
- "::std::basic_string_view", "::std::span");
+ // return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
+ // "::std::vector", "::std::basic_string",
+ // "::std::array");
+ return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
+ "::std::vector", "::std::basic_string", "::std::array",
+ "::std::basic_string_view", "::std::span");
}();
auto AllowedContainerTypeM = hasUnqualifiedDesugaredType(
@@ -61,19 +60,15 @@ template <typename RefsT> auto createPtrArgMatcher() {
hasTemplateArgument(0, templateArgument(refersToType(
qualType().bind(Refs.ValueType)))))))));
- auto CArrayTypeM = arrayType(hasElementType(qualType().bind(Refs.ValueType)));
+ auto CArrayTypeM =
+ constantArrayType(hasElementType(qualType().bind(Refs.ValueType)));
auto VariantContainerM =
expr(hasType(AllowedContainerTypeM)).bind(Refs.AsContainer);
- auto VariantCArrayM = expr(hasType(CArrayTypeM)).bind(Refs.AsCArray);
+ auto VariantCArrayM = expr(hasType(hasUnqualifiedDesugaredType(CArrayTypeM)))
+ .bind(Refs.AsCArray);
- auto VariantSmartPtrM =
- expr(hasType(pointerType(pointee(recordType(hasDeclaration(cxxRecordDecl(
- isSameOrDerivedFrom(classTemplateSpecializationDecl(
- hasAnyName("::std::unique_ptr", "::std::shared_ptr"),
- hasTemplateArgument(0, refersToType(CArrayTypeM)))))))))))
- .bind(Refs.AsSmartPtr);
auto VariantRawPtrM =
expr(hasType(pointerType(pointee(qualType().bind(Refs.ValueType)))));
@@ -100,17 +95,16 @@ template <typename RefsT> auto createPtrArgMatcher() {
// the last expr() in anyOf assumes previous matchers are ran eagerly from
// left to right, still need to test this is the actual behaviour
return expr(anyOf(StdDataMemberCallM, StdDataFreeCallM, VariantCArrayM,
- VariantSmartPtrM, VariantRawPtrM));
+ VariantRawPtrM));
}
namespace tag {
struct RawPtr {};
struct Container {};
struct CArray {};
-struct SmartPtr {};
} // namespace tag
struct PtrArg {
- std::variant<tag::RawPtr, tag::Container, tag::CArray, tag::SmartPtr> Tag;
+ std::variant<tag::RawPtr, tag::Container, tag::CArray> Tag;
const Expr &Node;
};
@@ -124,9 +118,6 @@ PtrArg extractNode(const CallExpr &CallNode,
if (const auto *Node = Result.Nodes.getNodeAs<Expr>(Refs.AsContainer);
Node != nullptr)
return {tag::Container{}, *Node};
- if (const auto *Node = Result.Nodes.getNodeAs<Expr>(Refs.AsSmartPtr);
- Node != nullptr)
- return {tag::SmartPtr{}, *Node};
return {tag::RawPtr{}, *CallNode.getArg(Refs.FallbackParameterIdx)};
}
@@ -136,14 +127,28 @@ const QualType *extractValueType(const MatchFinder::MatchResult &Result) {
const auto *MaybeRetType =
Result.Nodes.getNodeAs<QualType>(Refs.PtrCastFnReturnType);
const auto *ValueType = Result.Nodes.getNodeAs<QualType>(Refs.ValueType);
- llvm::errs() << "MaybeRetType: " << (MaybeRetType == nullptr) << '\n';
- llvm::errs() << "ValueType: " << (ValueType == nullptr) << '\n';
-
// checking equality is done here as opposed to when matching because the
// equalsBoundNode matcher depends on the match order and the
// PtrCastFnReturnType is only present in some scenarios
+ // if (MaybeRetType != nullptr)
+ // llvm::errs()
+ // << "MaybeRetType: "
+ // <<
+ // MaybeRetType->getCanonicalType().getUnqualifiedType().getAsString()
+ // << '\n';
+ // if (ValueType != nullptr)
+ // llvm::errs()
+ // << "ValueType: "
+ // << ValueType->getCanonicalType().getUnqualifiedType().getAsString()
+ // << '\n';
+
+ // stripping qualifiers is necessary in cases like when matching a call
+ // to const T* data() const; of a std::vector<char> -
+ // the container value_type (char) is not const qualified,
+ // but the return type of data() is.
if (MaybeRetType != nullptr and
- MaybeRetType->getCanonicalType() != ValueType->getCanonicalType())
+ MaybeRetType->getCanonicalType().getUnqualifiedType() !=
+ ValueType->getCanonicalType().getUnqualifiedType())
return nullptr;
return ValueType;
}
@@ -152,9 +157,9 @@ const QualType *extractValueType(const MatchFinder::MatchResult &Result) {
namespace dst {
constexpr size_t ArgIndex = 0;
struct RefT {
- static constexpr ptrarg::Refs Refs = {
- "Dst::AsContainer", "Dst::AsCArray", 0, "Dst::ValueType",
- "Dst::PtrCastFnReturnType", "Dst::AsSmartPtr"};
+ static constexpr ptrarg::Refs Refs = {"Dst::AsContainer", "Dst::AsCArray", 0,
+ "Dst::ValueType",
+ "Dst::PtrCastFnReturnType"};
};
auto createMatcher() { return ptrarg::createPtrArgMatcher<RefT>(); }
@@ -171,9 +176,9 @@ namespace src {
constexpr size_t ArgIndex = 1;
struct SrcRefsT {
- static constexpr ptrarg::Refs Refs = {
- "Src::AsContainer", "Src::AsCArray", 1, "Src::ValueType",
- "Src::PtrCastFnReturnType", "Src::AsSmartPtr"};
+ static constexpr ptrarg::Refs Refs = {"Src::AsContainer", "Src::AsCArray", 1,
+ "Src::ValueType",
+ "Src::PtrCastFnReturnType"};
};
auto createMatcher() { return ptrarg::createPtrArgMatcher<SrcRefsT>(); }
@@ -196,29 +201,25 @@ constexpr size_t ArgIndex = 2;
// 5. other expr
namespace variant {
struct SizeOfExpr {
+ const Expr *N = nullptr;
const Expr &Arg;
};
-struct NSizeOfExpr {
- const Expr &N;
- const Expr &Arg;
+struct SizeOfType {
+ const Expr *N = nullptr;
+ const QualType T;
};
struct Strlen {
const Expr &Arg;
};
} // namespace variant
-using SizeArg = std::variant<variant::SizeOfExpr, variant::NSizeOfExpr,
- variant::Strlen, const Expr *>;
+using SizeArg = std::variant<const Expr *, variant::SizeOfExpr,
+ variant::SizeOfType, variant::Strlen>;
struct SizeComponents {
CharUnits Unit;
std::string NExpr;
};
-struct IArg {
- virtual ~IArg() = default;
- virtual SizeComponents extractSizeComponents() = 0;
-};
-
static constexpr struct Refs {
llvm::StringLiteral SizeOfArg;
llvm::StringLiteral N;
@@ -230,35 +231,39 @@ static constexpr struct Refs {
};
auto createMatcher() {
- auto NSizeOfExprM =
- binaryOperator(hasOperatorName("*"),
- hasOperands(expr().bind(Refs.N),
- sizeOfExpr(has(expr().bind(Refs.SizeOfArg)))));
+ auto SizeOfExprM = sizeOfExpr(expr().bind(Refs.SizeOfArg));
+
+ auto NSizeOfExprM = binaryOperator(
+ hasOperatorName("*"), hasOperands(expr().bind(Refs.N), SizeOfExprM));
+
auto StrlenM =
callExpr(callee(functionDecl(hasAnyName(
"::strlen", "::std::strlen", "::wcslen", "::std::wcslen",
"::strnlen_s", "::std::strnlen_s", "::wcsnlen_s",
"::std::wcsnlen_s"))),
hasArgument(0, expr().bind(Refs.StrlenArg)));
+
auto StrlenPlusOneM = binaryOperator(
hasOperatorName("+"), hasOperands(StrlenM, integerLiteral(equals(1))));
- auto SizeOfExprM = sizeOfExpr(has(expr().bind(Refs.SizeOfArg)));
-
return expr(anyOf(NSizeOfExprM, StrlenM, StrlenPlusOneM, SizeOfExprM));
}
SizeArg extractNode(const CallExpr &CallNode,
const MatchFinder::MatchResult &Result) {
+ llvm::errs() << "Dumps Size:\n";
+ CallNode.dump();
llvm::errs() << __LINE__ << '\n';
- if (const auto *SizeOfArgNode = Result.Nodes.getNodeAs<Expr>(Refs.SizeOfArg);
- SizeOfArgNode != nullptr) {
+ if (const auto *SizeOfExprNode =
+ Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>(Refs.SizeOfArg);
+ SizeOfExprNode != nullptr) {
llvm::errs() << __LINE__ << '\n';
- if (const auto *NNode = Result.Nodes.getNodeAs<Expr>(Refs.N);
- NNode != nullptr)
- return variant::NSizeOfExpr{*NNode, *SizeOfArgNode};
+ const auto *NNode = Result.Nodes.getNodeAs<Expr>(Refs.N);
+ if (const auto *ArgAsExpr = SizeOfExprNode->getArgumentExpr();
+ ArgAsExpr != nullptr)
+ return variant::SizeOfExpr{NNode, *ArgAsExpr};
llvm::errs() << __LINE__ << '\n';
- return variant::SizeOfExpr{*SizeOfArgNode};
+ return variant::SizeOfType{NNode, SizeOfExprNode->getTypeOfArgument()};
}
llvm::errs() << __LINE__ << '\n';
if (const auto *StrlenArgNode = Result.Nodes.getNodeAs<Expr>(Refs.StrlenArg);
@@ -327,12 +332,8 @@ void ReplaceWithStdCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}
void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
+ llvm::errs() << __LINE__ << '\n';
const auto &CallNode = *Result.Nodes.getNodeAs<CallExpr>(ExpressionRef);
- llvm::errs() << __LINE__ << ' '
- << Lexer::getSourceText(
- CharSourceRange::getTokenRange(CallNode.getSourceRange()),
- *Result.SourceManager, getLangOpts())
- << '\n';
auto Dst = dst::extractNode(CallNode, Result);
auto Src = src::extractNode(CallNode, Result);
@@ -351,23 +352,52 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
*Result.SourceManager, getLangOpts())
.str();
};
+ llvm::errs() << "Call: " << ExprAsString(CallNode) << '\n';
// only have this function return a non-empty optional if the form of the size
// argument strongly indicates collection-related usage
auto ExtractComponents = [&]() -> std::optional<size::SizeComponents> {
+ llvm::errs() << __LINE__ << '\n';
+ if (const auto *NSizeOfTypeNode =
+ std::get_if<size::variant::SizeOfType>(&Size);
+ NSizeOfTypeNode != nullptr) {
+ llvm::errs() << __LINE__ << '\n';
+ auto &[N, T] = *NSizeOfTypeNode;
+
+ auto WidthT = Result.Context->getTypeSizeInChars(T);
+ auto StrN = N == nullptr ? "1" : ExprAsString(*N);
+
+ return {{WidthT, StrN}};
+ }
if (const auto *NSizeOfExprNode =
- std::get_if<size::variant::NSizeOfExpr>(&Size);
+ std::get_if<size::variant::SizeOfExpr>(&Size);
NSizeOfExprNode != nullptr) {
+ llvm::errs() << __LINE__ << '\n';
auto &[N, Arg] = *NSizeOfExprNode;
+ if (Arg.getType()->isConstantArrayType()) {
+ }
auto SizeOfArgWidth = Result.Context->getTypeSizeInChars(Arg.getType());
+ llvm::errs() << SizeOfArgWidth.getQuantity() << '\n';
return {{SizeOfArgWidth, ExprAsString(N)}};
}
+ // if (const auto *NSizeOfExprNode =
+ // std::get_if<size::variant::SizeOfExpr>(&Size);
+ // NSizeOfExprNode != nullptr) {
+ // llvm::errs() << __LINE__ << '\n' << "chuj\n";
+ // auto &[N, Arg] = *NSizeOfExprNode;
+ // auto SizeOfArgWidth =
+ // Result.Context->getTypeSizeInChars(Arg.getType()); llvm::errs() <<
+ // SizeOfArgWidth.getQuantity() << '\n'; return {{SizeOfArgWidth,
+ // ExprAsString(N)}};
+ // }
if (const auto *StrlenNode = std::get_if<size::variant::Strlen>(&Size);
StrlenNode != nullptr) {
+ llvm::errs() << __LINE__ << '\n';
auto StrlenArgTypeWidth = Result.Context->getTypeSizeInChars(
StrlenNode->Arg.getType()->getPointeeType());
return {{StrlenArgTypeWidth, ExprAsString(*CallNode.getArg(2))}};
}
+ llvm::errs() << __LINE__ << '\n';
return std::nullopt;
};
@@ -427,10 +457,15 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
return "std::begin(" + AsString + ")";
}();
- llvm::errs() << "SrcFixit: " << SrcFixit << '\n'
- << "DstFixit: " << DstFixit << '\n';
- auto CalleeIsWideVariant =
- CallNode.getDirectCallee()->getParamDecl(0)->getType()->isWideCharType();
+ auto CalleeIsWideVariant = [&]() {
+ const auto *Callee = CallNode.getDirectCallee();
+ if (Callee == nullptr)
+ return false;
+ auto *ParamDecl = Callee->getParamDecl(0);
+ if (ParamDecl == nullptr)
+ return false;
+ return ParamDecl->getType()->getPointeeType()->isWideCharType();
+ }();
auto CalleeUnit = [&]() {
if (CalleeIsWideVariant) {
@@ -439,33 +474,22 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
}
return CharUnits::One();
}();
- llvm::errs() << __LINE__ << Size.index() << '\n';
- llvm::errs() << __LINE__ << " CalleeUnit: " << CalleeUnit.getQuantity()
- << '\n';
auto SizeFixit = [&]() -> std::string {
- if (ValueTypeWidth == CalleeUnit) {
- // replace sizeof(CArray) with the more modern std::size(CArray)
- if (const auto *SizeOfExprNode =
- std::get_if<size::variant::SizeOfExpr>(&Size);
- SizeOfExprNode != nullptr and
- SizeOfExprNode->Arg.getType()->isArrayType()) {
- return "std::size(" + ExprAsString(SizeOfExprNode->Arg) + ")";
- }
-
- return ExprAsString(*CallNode.getArg(size::ArgIndex));
- }
// try to factor out the unit from the size expression
+ llvm::errs() << __LINE__ << '\n';
if (auto MaybeSizeComponents = ExtractComponents();
- MaybeSizeComponents.has_value() and
- MaybeSizeComponents->Unit == ValueTypeWidth) {
+ MaybeSizeComponents.has_value()) {
+
+ // __jm__ TODO
+ llvm::errs() << __LINE__ << '\n';
return MaybeSizeComponents->NExpr;
}
// last resort, divide by ValueTypeWidth
+ llvm::errs() << __LINE__ << '\n';
return ExprAsString(*CallNode.getArg(size::ArgIndex)) + " / " + "sizeof(" +
DstVT->getAsString() + ")";
}();
- llvm::errs() << "SizeFixit: " << SizeFixit << '\n';
Diag << FixItHint::CreateRemoval(CallNode.getSourceRange())
<< FixItHint::CreateInsertion(CallNode.getBeginLoc(),
@@ -475,15 +499,10 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
Result.SourceManager->getFileID(CallNode.getBeginLoc()),
"<algorithm>");
- // Modern containers will include a std::[c]begin declaration with their
- // header, but not c-arrays (or smart pointers to c-arrays), so need to ensure
- // they are included.
- auto IsCArrayOrWrapper = [](const decltype(ptrarg::PtrArg::Tag) &Tag) {
- return std::holds_alternative<ptrarg::tag::CArray>(Tag) or
- std::holds_alternative<ptrarg::tag::SmartPtr>(Tag);
- };
-
- if (IsCArrayOrWrapper(Dst.Tag) and IsCArrayOrWrapper(Src.Tag))
+ // All containers will contain an std::[c]begin declaration with their
+ // definition, with the exception of constant c-arrays
+ if (std::holds_alternative<ptrarg::tag::CArray>(Dst.Tag) and
+ std::holds_alternative<ptrarg::tag::CArray>(Src.Tag))
Diag << Inserter.createIncludeInsertion(
Result.SourceManager->getFileID(CallNode.getBeginLoc()), "<iterator>");
@@ -491,7 +510,8 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
// std::get_if<size::variant::NSizeOfExpr>(&size);
// NSizeofExprNode != nullptr) {
// auto &[N, Arg] = *NSizeofExprNode;
- // auto SizeOfArgWidth = Result.Context->getTypeSizeInChars(Arg->getType());
+ // auto SizeOfArgWidth =
+ // Result.Context->getTypeSizeInChars(Arg->getType());
// issueFixitIfWidthsMatch(dst.Node, src.Node,
// {SizeOfArgWidth, ExprAsString(N)});
// return;
@@ -516,14 +536,17 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
// return;
// }
// // __jm__ weird bc we assume dst and src are collections
- // // if Arg turns out to have the same type as dst or src then just suggest
+ // // if Arg turns out to have the same type as dst or src then just
+ // suggest
// // copy via the assignment operator
- // if (auto argType = Arg->getType(); argType == DstVT or argType == SrcVT)
+ // if (auto argType = Arg->getType(); argType == DstVT or argType ==
+ // SrcVT)
// {
// issueFixit{ValueTypeWidth, ExprAsString(CallNode.getArg(2))};
// return;
// }
- // // __jm__ only flag this as suspicious with a further explanation in the
+ // // __jm__ only flag this as suspicious with a further explanation in
+ // the
// // diagnostic TODO: a sizeof of an unrelated type when copying between
// // collections does not make a lot of sense
>From 80450aadafd5108b8824b9ce914d3e1b0168e6a4 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Tue, 7 Jan 2025 21:45:30 +0100
Subject: [PATCH 10/15] Add more tests
---
.../modernize/replace-with-std-copy.cpp | 156 ++++++++++--------
1 file changed, 88 insertions(+), 68 deletions(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
index 8927e4b1da2e462..2dc4efcc65971af 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/replace-with-std-copy.cpp
@@ -1,52 +1,6 @@
// RUN: %check_clang_tidy %s modernize-replace-with-std-copy %t -- \
// RUN: -config='{CheckOptions: {modernize-replace-with-std-copy.FlagMemcpy: true}}'
-// possible call scenarios, infeasible to cover all
-// [X] all free
-// source:
-// type:
-// [ ] c-array
-// std::data:
-// [ ] free
-// [ ] N/A
-// [ ] container
-// std::data:
-// [ ] free
-// [ ] member
-// type:
-// [ ] std::vector<T>
-// [ ] std::span<T>
-// [ ] std::deque<T>
-// [ ] std::array<T, _>
-// [ ] std::string
-// [ ] std::string_view
-// dest:
-// type:
-// [ ] T[]
-// std::data:
-// [ ] free
-// [ ] N/A
-// [ ] container
-// std::data:
-// [ ] free
-// [ ] member
-// type:
-// [ ] std::vector<T>
-// [ ] std::span<T>
-// [ ] std::deque<T>
-// [ ] std::array<T, _>
-// [ ] std::string
-// [ ] std::string_view
-// callee:
-// name:
-// [ ] memmove
-// [ ] memcpy
-// [ ] wmemmove
-// [ ] wmemcpy
-// qualified:
-// [ ] y
-// [ ] n
-
namespace std {
using size_t = decltype(sizeof(int));
@@ -62,6 +16,7 @@ class char_traits {};
template <typename C, typename T, typename A>
struct basic_string {
typedef basic_string<C, T, A> _Type;
+ using value_type = C;
basic_string();
basic_string(const C* p, const A& a = A());
@@ -140,38 +95,33 @@ typename Container::value_type* data(Container& Arg) {
return Arg.data();
}
-template<typename T>
-T* data(T Arg[]);
-
template<typename Container>
const typename Container::value_type* data(const Container& Arg) {
return Arg.data();
}
-template<typename T>
-const T* data(const T[]);
+template<typename T, size_t N>
+constexpr T* data(T (&arr)[N]) {
+ return arr;
+}
-size_t size(void *);
+template< class T, std::size_t N >
+constexpr std::size_t size( const T (&array)[N] ) {
+ return N;
+}
template<typename Container>
size_t size(const Container& c) {
return c.size();
}
-size_t strlen(const char *);
-
wchar_t* wmemcpy( wchar_t* dest, const wchar_t* src, size_t count );
wchar_t* wmemmove( wchar_t* dest, const wchar_t* src, size_t count );
template <typename T>
struct unique_ptr {
unique_ptr(T);
- T *get() const;
- explicit operator bool() const;
- void reset(T *ptr);
T &operator*() const;
- T *operator->() const;
- T& operator[](size_t i) const;
};
} // namespace std
@@ -236,7 +186,7 @@ void notSupportedEx() {
const char *Src = "once upon a daydream...";
char *Dst = new char[64];
- std::memcpy(Dst, Src, std::strlen(Src));
+ std::memcpy(Dst, Src, 24);
}
}
@@ -245,19 +195,19 @@ void supportedEx() {
wchar_t Src[] = L"once upon a daydream...";
wchar_t Dst[4];
- std::wmemcpy(Dst, Src, sizeof Dst);
+ std::wmemcpy(Dst, Src, sizeof(Dst) / sizeof(wchar_t));
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemcpy'
// CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Dst), std::begin(Dst));
- std::wmemcpy(Dst, Src, sizeof(Dst));
+ std::wmemcpy(Dst, Src, sizeof(Dst) / sizeof(wchar_t));
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemcpy'
// CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Dst), std::begin(Dst));
- wmemcpy(Dst, Src, sizeof Src);
+ wmemcpy(Dst, Src, std::size(Src));
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemcpy'
// CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Src), std::begin(Dst));
- wmemmove(Dst, Src, sizeof(Src));
+ wmemmove(Dst, Src, std::size(Src));
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemmove'
// CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Src), std::begin(Dst));
}
@@ -280,11 +230,21 @@ void supportedEx() {
// CHECK-FIXES: std::copy_n(std::cbegin(Src), Src.size(), std::begin(Dst));
}
+ { // reference to const c-array
+ const char Src[] = "once upon a daydream...";
+ char Dst[7];
+ decltype(Dst)& RefDst = Dst;
+
+ memcpy(RefDst, Src, sizeof(RefDst));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(RefDst), std::begin(RefDst));
+ }
+
{ // std::wstring + std::unique_ptr
std::wstring Src = L"once upon a daydream...";
- std::unique_ptr<wchar_t[16]> Dst(new wchar_t[16]);
+ std::unique_ptr<wchar_t[7]> Dst(new wchar_t[7]);
- std::wmemcpy(*Dst, Src.data(), sizeof(*Dst));
+ std::wmemcpy(*Dst, Src.data(), sizeof(*Dst) / sizeof(wchar_t));
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemcpy'
// CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(*Dst), std::begin(*Dst));
}
@@ -308,7 +268,7 @@ void supportedEx() {
// CHECK-FIXES: std::copy_n(std::cbegin(Src), Src.size() - 2, std::begin(Dst));
}
- { // NSizeOfExpr cases
+ { // various size arg cases
std::int32_t Data[] = {1, 2, 3, 4, 1, 2, 3, 4};
std::span<std::int32_t> Src{Data};
std::vector<std::int32_t> Dst(8);
@@ -318,8 +278,68 @@ void supportedEx() {
// CHECK-FIXES: std::copy_n(std::cbegin(Src), Dst.size(), std::begin(Dst));
memmove(std::data(Dst), std::data(Src), sizeof(int) * std::size(Dst));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Dst), std::begin(Dst));
+
+ // If both arguments of the product match sizeOfExpr then the right one is lifted
+ std::memcpy(std::data(Dst), std::data(Src), sizeof(int) * sizeof(std::int32_t));
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memcpy'
- // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Dst), std::begin(Dst));
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), sizeof(int), std::begin(Dst));
+
+ std::memcpy(std::data(Dst), std::data(Src), sizeof(decltype(Dst)::value_type) * 3);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), 3, std::begin(Dst));
+ }
+
+ { // default sizearg path
+ std::vector<std::int32_t> Src(64);
+ std::u32string Dst = U"once upon a daydream...";
+
+ // Don't factor out sizeof expressions that are unlikely to be related to the value type,
+ // this might lose semantic info
+ memmove(std::data(Dst), std::data(Src), sizeof(float) * 3);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), (sizeof(float) * 3) / sizeof(char32_t), std::begin(Dst));
+
+ // the sizeof(value_type) is nested too deep in the product expression,
+ // so currently the check will leave it as is
+ memmove(Dst.data(), Src.data(), 1 * 2 * sizeof(char32_t) * 4);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memmove'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), (1 * 2 * sizeof(char32_t) * 4) / sizeof(char32_t), std::begin(Dst));
+
+ std::memcpy(Dst.data(), Src.data(), sizeof(char32_t) + sizeof(int));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), (sizeof(char32_t) + sizeof(int)) / sizeof(char32_t), std::begin(Dst));
+ }
+
+ { // SizeOfDivSizeOf - width = char
+ const char Src[] = "once upon a daydream...";
+ std::string DstStr = " ";
+
+ // superfluous, can be simplified
+ std::memcpy(DstStr.data(), std::data(Src), sizeof(Src) / sizeof(char));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Src), std::begin(DstStr));
+ }
+
+ { // SizeOfDivSizeOf - width = int
+ const int Src[] = {1, 2, 3, 4, 5, 6, 7, 8};
+ std::vector<int> DstVec(8);
+
+ // this is probably a bug, but the check prefers not to change behaviour
+ std::memcpy(DstVec.data(), std::data(Src), sizeof(Src) / sizeof(int));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'memcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), (sizeof(Src) / sizeof(int)) / sizeof(int), std::begin(DstVec));
+ }
+
+ { // SizeOfDivSizeOf - width = wchar_t
+ const wchar_t Src[] = L"once upon a daydream...";
+ std::wstring DstStr = L" ";
+
+ // simplifies
+ std::wmemcpy(DstStr.data(), std::data(Src), sizeof(Src) / sizeof(wchar_t));
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: prefer std::copy_n to 'wmemcpy'
+ // CHECK-FIXES: std::copy_n(std::cbegin(Src), std::size(Src), std::begin(DstStr));
}
}
} // namespace
\ No newline at end of file
>From 47a371b0acba71dab6cac229a16f6351d24af6a5 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Tue, 7 Jan 2025 21:46:10 +0100
Subject: [PATCH 11/15] Finish size semantics, clean up comments and logs
---
.../modernize/ReplaceWithStdCopyCheck.cpp | 493 ++++++------------
1 file changed, 173 insertions(+), 320 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
index 28d755e6767f373..a718937614d1a61 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
@@ -27,7 +27,7 @@ namespace clang::tidy {
namespace modernize {
namespace {
-constexpr llvm::StringLiteral ExpressionRef = "::std::memcpy";
+constexpr llvm::StringLiteral ExpressionRef = "ReplaceWithStdCopyCheckRef";
constexpr llvm::StringLiteral ReturnValueDiscardedRef =
"ReturnValueDiscardedRef";
@@ -40,8 +40,7 @@ struct Refs {
llvm::StringLiteral ValueType;
llvm::StringLiteral PtrCastFnReturnType;
};
-// rn matchers have a problem with binding types correctly, clang-query build up
-// the matchers from this file tmrw for debugging
+
template <typename RefsT> auto createPtrArgMatcher() {
constexpr Refs Refs = RefsT::Refs;
@@ -69,16 +68,11 @@ template <typename RefsT> auto createPtrArgMatcher() {
auto VariantCArrayM = expr(hasType(hasUnqualifiedDesugaredType(CArrayTypeM)))
.bind(Refs.AsCArray);
- auto VariantRawPtrM =
- expr(hasType(pointerType(pointee(qualType().bind(Refs.ValueType)))));
-
auto StdDataReturnM =
returns(pointerType(pointee(qualType().bind(Refs.PtrCastFnReturnType))));
auto StdDataMemberDeclM =
cxxMethodDecl(hasName("data"), parameterCountIs(0), StdDataReturnM);
- // ,unless(isConst()) // __jm__ ONLY difference between Dest
- // and Source calls, but maybe don't include it?
auto StdDataFreeDeclM = functionDecl(hasAnyName("::std::data", "::data"),
parameterCountIs(1), StdDataReturnM);
@@ -94,8 +88,7 @@ template <typename RefsT> auto createPtrArgMatcher() {
// the last expr() in anyOf assumes previous matchers are ran eagerly from
// left to right, still need to test this is the actual behaviour
- return expr(anyOf(StdDataMemberCallM, StdDataFreeCallM, VariantCArrayM,
- VariantRawPtrM));
+ return expr(anyOf(StdDataMemberCallM, StdDataFreeCallM, VariantCArrayM));
}
namespace tag {
@@ -124,26 +117,17 @@ PtrArg extractNode(const CallExpr &CallNode,
template <typename RefT>
const QualType *extractValueType(const MatchFinder::MatchResult &Result) {
constexpr Refs Refs = RefT::Refs;
+
+ // checking equality is done here as opposed to when matching because the
+ // equalsBoundNode matcher depends on the match order and the
+ // PtrCastFnReturnType is only present in some scenarios,
+ // making it tricky to swap the binding order
const auto *MaybeRetType =
Result.Nodes.getNodeAs<QualType>(Refs.PtrCastFnReturnType);
const auto *ValueType = Result.Nodes.getNodeAs<QualType>(Refs.ValueType);
- // checking equality is done here as opposed to when matching because the
- // equalsBoundNode matcher depends on the match order and the
- // PtrCastFnReturnType is only present in some scenarios
- // if (MaybeRetType != nullptr)
- // llvm::errs()
- // << "MaybeRetType: "
- // <<
- // MaybeRetType->getCanonicalType().getUnqualifiedType().getAsString()
- // << '\n';
- // if (ValueType != nullptr)
- // llvm::errs()
- // << "ValueType: "
- // << ValueType->getCanonicalType().getUnqualifiedType().getAsString()
- // << '\n';
// stripping qualifiers is necessary in cases like when matching a call
- // to const T* data() const; of a std::vector<char> -
+ // to const T* vector<char>::data() const;
// the container value_type (char) is not const qualified,
// but the return type of data() is.
if (MaybeRetType != nullptr and
@@ -157,8 +141,8 @@ const QualType *extractValueType(const MatchFinder::MatchResult &Result) {
namespace dst {
constexpr size_t ArgIndex = 0;
struct RefT {
- static constexpr ptrarg::Refs Refs = {"Dst::AsContainer", "Dst::AsCArray", 0,
- "Dst::ValueType",
+ static constexpr ptrarg::Refs Refs = {"Dst::AsContainer", "Dst::AsCArray",
+ ArgIndex, "Dst::ValueType",
"Dst::PtrCastFnReturnType"};
};
@@ -176,8 +160,8 @@ namespace src {
constexpr size_t ArgIndex = 1;
struct SrcRefsT {
- static constexpr ptrarg::Refs Refs = {"Src::AsContainer", "Src::AsCArray", 1,
- "Src::ValueType",
+ static constexpr ptrarg::Refs Refs = {"Src::AsContainer", "Src::AsCArray",
+ ArgIndex, "Src::ValueType",
"Src::PtrCastFnReturnType"};
};
@@ -193,92 +177,100 @@ auto extractValueType(const MatchFinder::MatchResult &Result) {
namespace size {
constexpr size_t ArgIndex = 2;
-// cases:
-// 1. sizeof(T) where T is a type
-// 2. sizeof(rec) where rec is a CArray
-// 3. N * sizeof(T)
-// 4. strlen(rec) [+ 1]
-// 5. other expr
+
namespace variant {
-struct SizeOfExpr {
- const Expr *N = nullptr;
- const Expr &Arg;
+struct SizeOfCArray {
+ const Expr &Array;
};
-struct SizeOfType {
- const Expr *N = nullptr;
- const QualType T;
+struct NSizeOfType {
+ const Expr &N;
+ const QualType &Arg;
};
-struct Strlen {
- const Expr &Arg;
+struct SizeOfDivSizeOf {
+ const Expr &Array;
+ const QualType &DivSizeOfType;
};
} // namespace variant
-using SizeArg = std::variant<const Expr *, variant::SizeOfExpr,
- variant::SizeOfType, variant::Strlen>;
-
-struct SizeComponents {
- CharUnits Unit;
- std::string NExpr;
-};
+using SizeArg = std::variant<const Expr *, variant::SizeOfCArray,
+ variant::NSizeOfType, variant::SizeOfDivSizeOf>;
static constexpr struct Refs {
- llvm::StringLiteral SizeOfArg;
- llvm::StringLiteral N;
- llvm::StringLiteral StrlenArg;
-} Refs = {
- "Size::SizeOfExpr",
- "Size::N",
- "Size::StrlenArg",
-};
+ llvm::StringLiteral SizeOfCArray;
+ llvm::StringLiteral NSizeOfTypeN;
+ llvm::StringLiteral NSizeOfTypeArg;
+ llvm::StringLiteral SizeOfDivSizeOfArray;
+ llvm::StringLiteral SizeOfDivSizeOfArg;
+} Refs = {"Size::SizeOfCArray", "Size::NSizeOfTypeN", "Size::NSizeOfTypeArg",
+ "Size::SizeOfDivSizeOfArray", "Size::SizeOfDivSizeOfArg"};
auto createMatcher() {
- auto SizeOfExprM = sizeOfExpr(expr().bind(Refs.SizeOfArg));
+ // NOTE: this check does not detect common, invalid patterns
+ // like sizeof(_) * sizeof(_), etc. since other checks exist for those.
+
+ // patterns of the size argument that may be modified :
+ // 1. sizeof(arr)
+ // - invalid if callee is a wide variant,
+ // should be sizeof(arr) / sizeof(wchar_like)
+ // - otherwise -> std::size(arr)
+ // 2. N * sizeof(value_like)
+ // - invalid if callee is a wide variant, should just be N
+ // - otherwise when sizeof(value_like) == sizeof(value_type) -> N
+ // 3. sizeof(arr) / sizeof(value_like)
+ // - valid if callee is a wide variant -> std::size(arr)
+ // - valid if sizeof(value_like) == 1
+ // - invalid otherwise, will fall back to (expr) / sizeof(value_type)
+
+ constexpr auto SizeOfCArray = [](llvm::StringLiteral Ref) {
+ return sizeOfExpr(
+ has(expr(hasType(hasUnqualifiedDesugaredType(constantArrayType())))
+ .bind(Ref)));
+ };
- auto NSizeOfExprM = binaryOperator(
- hasOperatorName("*"), hasOperands(expr().bind(Refs.N), SizeOfExprM));
+ constexpr auto SizeOfType = [](llvm::StringLiteral Ref) {
+ return sizeOfExpr(hasArgumentOfType(qualType().bind(Ref)));
+ };
- auto StrlenM =
- callExpr(callee(functionDecl(hasAnyName(
- "::strlen", "::std::strlen", "::wcslen", "::std::wcslen",
- "::strnlen_s", "::std::strnlen_s", "::wcsnlen_s",
- "::std::wcsnlen_s"))),
- hasArgument(0, expr().bind(Refs.StrlenArg)));
+ auto NSizeOfTypeM = binaryOperator(
+ hasOperatorName("*"), hasOperands(expr().bind(Refs.NSizeOfTypeN),
+ SizeOfType(Refs.NSizeOfTypeArg)));
- auto StrlenPlusOneM = binaryOperator(
- hasOperatorName("+"), hasOperands(StrlenM, integerLiteral(equals(1))));
+ auto SizeOfDivSizeOfM = binaryOperator(
+ hasOperatorName("/"), hasOperands(SizeOfCArray(Refs.SizeOfDivSizeOfArray),
+ SizeOfType(Refs.SizeOfDivSizeOfArg)));
- return expr(anyOf(NSizeOfExprM, StrlenM, StrlenPlusOneM, SizeOfExprM));
+ return expr(
+ anyOf(NSizeOfTypeM, SizeOfCArray(Refs.SizeOfCArray), SizeOfDivSizeOfM));
}
SizeArg extractNode(const CallExpr &CallNode,
const MatchFinder::MatchResult &Result) {
- llvm::errs() << "Dumps Size:\n";
- CallNode.dump();
- llvm::errs() << __LINE__ << '\n';
- if (const auto *SizeOfExprNode =
- Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>(Refs.SizeOfArg);
- SizeOfExprNode != nullptr) {
- llvm::errs() << __LINE__ << '\n';
- const auto *NNode = Result.Nodes.getNodeAs<Expr>(Refs.N);
- if (const auto *ArgAsExpr = SizeOfExprNode->getArgumentExpr();
- ArgAsExpr != nullptr)
- return variant::SizeOfExpr{NNode, *ArgAsExpr};
+ auto ConcreteCase = [&Nodes = Result.Nodes]() -> std::optional<SizeArg> {
+ if (const auto *Array = Nodes.getNodeAs<Expr>(Refs.SizeOfCArray);
+ Array != nullptr)
+ return variant::SizeOfCArray{*Array};
llvm::errs() << __LINE__ << '\n';
- return variant::SizeOfType{NNode, SizeOfExprNode->getTypeOfArgument()};
- }
- llvm::errs() << __LINE__ << '\n';
- if (const auto *StrlenArgNode = Result.Nodes.getNodeAs<Expr>(Refs.StrlenArg);
- StrlenArgNode != nullptr)
- return variant::Strlen{*StrlenArgNode};
- llvm::errs() << __LINE__ << '\n';
- return CallNode.getArg(2);
-}
-// 1. N * sizeof(type) (commutative) - issue warning but no fixit, or fixit
-// only if both src/dest are fixit friendly 1.1. This might allow fixits for
-// wider-than-byte element collections
-// 2. strlen(src|dest) ?(+ 1 (commutative)) -- issue warning but no fixit
-// 4. sizeof(variable) only if that variable is of arrayType, if it's of
-// ptrType that may indicate [de]serialization
+ if (const auto *N = Nodes.getNodeAs<Expr>(Refs.NSizeOfTypeN);
+ N != nullptr) {
+ if (const auto *Arg = Nodes.getNodeAs<QualType>(Refs.NSizeOfTypeArg);
+ Arg != nullptr)
+ return variant::NSizeOfType{*N, *Arg};
+ return std::nullopt;
+ }
+ if (const auto *Array = Nodes.getNodeAs<Expr>(Refs.SizeOfDivSizeOfArray);
+ Array != nullptr) {
+ if (const auto *SizeOfArg =
+ Nodes.getNodeAs<QualType>(Refs.SizeOfDivSizeOfArg);
+ SizeOfArg != nullptr)
+ return variant::SizeOfDivSizeOf{*Array, *SizeOfArg};
+ return std::nullopt;
+ }
+ return std::nullopt;
+ };
+ if (auto MaybeSize = ConcreteCase(); MaybeSize.has_value())
+ return *MaybeSize;
+ return CallNode.getArg(ArgIndex);
+}
} // namespace size
auto createCalleeMatcher(bool FlagMemcpy) {
@@ -354,53 +346,6 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
};
llvm::errs() << "Call: " << ExprAsString(CallNode) << '\n';
- // only have this function return a non-empty optional if the form of the size
- // argument strongly indicates collection-related usage
- auto ExtractComponents = [&]() -> std::optional<size::SizeComponents> {
- llvm::errs() << __LINE__ << '\n';
- if (const auto *NSizeOfTypeNode =
- std::get_if<size::variant::SizeOfType>(&Size);
- NSizeOfTypeNode != nullptr) {
- llvm::errs() << __LINE__ << '\n';
- auto &[N, T] = *NSizeOfTypeNode;
-
- auto WidthT = Result.Context->getTypeSizeInChars(T);
- auto StrN = N == nullptr ? "1" : ExprAsString(*N);
-
- return {{WidthT, StrN}};
- }
- if (const auto *NSizeOfExprNode =
- std::get_if<size::variant::SizeOfExpr>(&Size);
- NSizeOfExprNode != nullptr) {
- llvm::errs() << __LINE__ << '\n';
- auto &[N, Arg] = *NSizeOfExprNode;
- if (Arg.getType()->isConstantArrayType()) {
- }
- auto SizeOfArgWidth = Result.Context->getTypeSizeInChars(Arg.getType());
- llvm::errs() << SizeOfArgWidth.getQuantity() << '\n';
- return {{SizeOfArgWidth, ExprAsString(N)}};
- }
- // if (const auto *NSizeOfExprNode =
- // std::get_if<size::variant::SizeOfExpr>(&Size);
- // NSizeOfExprNode != nullptr) {
- // llvm::errs() << __LINE__ << '\n' << "chuj\n";
- // auto &[N, Arg] = *NSizeOfExprNode;
- // auto SizeOfArgWidth =
- // Result.Context->getTypeSizeInChars(Arg.getType()); llvm::errs() <<
- // SizeOfArgWidth.getQuantity() << '\n'; return {{SizeOfArgWidth,
- // ExprAsString(N)}};
- // }
- if (const auto *StrlenNode = std::get_if<size::variant::Strlen>(&Size);
- StrlenNode != nullptr) {
- llvm::errs() << __LINE__ << '\n';
- auto StrlenArgTypeWidth = Result.Context->getTypeSizeInChars(
- StrlenNode->Arg.getType()->getPointeeType());
- return {{StrlenArgTypeWidth, ExprAsString(*CallNode.getArg(2))}};
- }
- llvm::errs() << __LINE__ << '\n';
- return std::nullopt;
- };
-
bool DstIsRawPtr = std::holds_alternative<ptrarg::tag::RawPtr>(Dst.Tag);
bool SrcIsRawPtr = std::holds_alternative<ptrarg::tag::RawPtr>(Src.Tag);
@@ -423,6 +368,23 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
auto DstTypeWidth = Result.Context->getTypeSizeInChars(*DstVT);
auto SrcTypeWidth = Result.Context->getTypeSizeInChars(*SrcVT);
+ auto CalleeIsWideVariant = [&]() {
+ const auto *Callee = CallNode.getDirectCallee();
+ if (Callee == nullptr)
+ return false;
+ auto *ParamDecl = Callee->getParamDecl(0);
+ if (ParamDecl == nullptr)
+ return false;
+ return ParamDecl->getType()->getPointeeType()->isWideCharType();
+ }();
+
+ auto CalleeUnit = [&]() {
+ if (CalleeIsWideVariant)
+ return Result.Context->getTypeSizeInChars(
+ (Result.Context->getWideCharType()));
+ return CharUnits::One();
+ }();
+
auto CheckIsFixable = [&]() {
// If the width types differ, it's hard to reason about what would be a
// helpful replacement, so just don't issue a fixit in this case
@@ -435,6 +397,11 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
IsReturnValueUsed)
return false;
+ // for widechar variants assume that the value types are also
+ // of wchar_t width, to make analysis easier.
+ if (CalleeIsWideVariant and DstTypeWidth != CalleeUnit)
+ return false;
+
return true;
};
if (not CheckIsFixable())
@@ -457,44 +424,77 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
return "std::begin(" + AsString + ")";
}();
- auto CalleeIsWideVariant = [&]() {
- const auto *Callee = CallNode.getDirectCallee();
- if (Callee == nullptr)
- return false;
- auto *ParamDecl = Callee->getParamDecl(0);
- if (ParamDecl == nullptr)
- return false;
- return ParamDecl->getType()->getPointeeType()->isWideCharType();
- }();
+ // this function is used to specify when a type from a sizeof(_) call is
+ // considered equivalent to the value type of the collection.
+ // for now it is relaxed because the template specialization matcher
+ // seems to unqualify types automatically (e.g. the value type of
+ // std::vector<std::int32_t> will just be int) and
+ auto CheckIsEquivValueType = [&DstVT](QualType SizeArgType) {
+ return SizeArgType->getCanonicalTypeUnqualified() ==
+ (*DstVT)->getCanonicalTypeUnqualified();
+ };
- auto CalleeUnit = [&]() {
- if (CalleeIsWideVariant) {
- return Result.Context->getTypeSizeInChars(
- (Result.Context->getWideCharType()));
+ auto ByteModifySizeArg = [&]() -> std::string {
+ if (const auto *SizeOfCArray =
+ std::get_if<size::variant::SizeOfCArray>(&Size);
+ SizeOfCArray != nullptr) {
+ // simply replaces sizeof(arr) with std::size(arr)
+ return "std::size(" + ExprAsString(SizeOfCArray->Array) + ")";
+ }
+ if (const auto *NSizeofExprNode =
+ std::get_if<size::variant::NSizeOfType>(&Size);
+ NSizeofExprNode != nullptr) {
+ auto &[N, Arg] = *NSizeofExprNode;
+ // In this case it is easy to factor out the byte multiplier
+ // by just dropping the sizeof expression from the size computation
+ if (CheckIsEquivValueType(Arg))
+ return ExprAsString(N);
+ }
+ if (const auto *SizeOfDivSizeOf =
+ std::get_if<size::variant::SizeOfDivSizeOf>(&Size);
+ SizeOfDivSizeOf != nullptr) {
+ auto &[Array, DivSizeOfType] = *SizeOfDivSizeOf;
+ if (CheckIsEquivValueType(DivSizeOfType) and ValueTypeWidth == CalleeUnit)
+ return "std::size(" + ExprAsString(Array) + ")";
}
- return CharUnits::One();
- }();
- auto SizeFixit = [&]() -> std::string {
- // try to factor out the unit from the size expression
- llvm::errs() << __LINE__ << '\n';
- if (auto MaybeSizeComponents = ExtractComponents();
- MaybeSizeComponents.has_value()) {
+ // In the specific case where the collections' value types are byte-wide,
+ // no unit conversion of the size argument is necessary
+ if (ValueTypeWidth == CalleeUnit)
+ return ExprAsString(*CallNode.getArg(size::ArgIndex));
+
+ // For all other cases where the value type is wider than one byte,
+ // and the size argument is of a form that is not easy to factor the unit
+ // out of, perform explicit division to ensure it is in element units
+ return (llvm::Twine("") + "(" +
+ ExprAsString(*CallNode.getArg(size::ArgIndex)) + ") / sizeof(" +
+ DstVT->getAsString() + ")")
+ .str();
+ };
- // __jm__ TODO
- llvm::errs() << __LINE__ << '\n';
- return MaybeSizeComponents->NExpr;
+ auto WideModifySizeArg = [&]() {
+ if (const auto *SizeOfDivSizeOf =
+ std::get_if<size::variant::SizeOfDivSizeOf>(&Size);
+ SizeOfDivSizeOf != nullptr) {
+ auto &[Array, DivSizeOfType] = *SizeOfDivSizeOf;
+ if (CheckIsEquivValueType(DivSizeOfType))
+ return "std::size(" + ExprAsString(Array) + ")";
}
- // last resort, divide by ValueTypeWidth
- llvm::errs() << __LINE__ << '\n';
- return ExprAsString(*CallNode.getArg(size::ArgIndex)) + " / " + "sizeof(" +
- DstVT->getAsString() + ")";
- }();
+ // Since we assume wide variants' value type width equals wchar_t,
+ // the units should already be unified and no modifications to the size
+ // argument are necessary
+ return ExprAsString(*CallNode.getArg(size::ArgIndex));
+ };
+
+ auto SizeFixit =
+ CalleeIsWideVariant ? WideModifySizeArg() : ByteModifySizeArg();
Diag << FixItHint::CreateRemoval(CallNode.getSourceRange())
<< FixItHint::CreateInsertion(CallNode.getBeginLoc(),
- "std::copy_n(" + SrcFixit + ", " +
- SizeFixit + ", " + DstFixit + ");")
+ (llvm::Twine() + "std::copy_n(" +
+ SrcFixit + ", " + SizeFixit + ", " +
+ DstFixit + ")")
+ .str())
<< Inserter.createIncludeInsertion(
Result.SourceManager->getFileID(CallNode.getBeginLoc()),
"<algorithm>");
@@ -505,153 +505,6 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
std::holds_alternative<ptrarg::tag::CArray>(Src.Tag))
Diag << Inserter.createIncludeInsertion(
Result.SourceManager->getFileID(CallNode.getBeginLoc()), "<iterator>");
-
- // if (const auto *NSizeofExprNode =
- // std::get_if<size::variant::NSizeOfExpr>(&size);
- // NSizeofExprNode != nullptr) {
- // auto &[N, Arg] = *NSizeofExprNode;
- // auto SizeOfArgWidth =
- // Result.Context->getTypeSizeInChars(Arg->getType());
- // issueFixitIfWidthsMatch(dst.Node, src.Node,
- // {SizeOfArgWidth, ExprAsString(N)});
- // return;
- // }
- // if (const auto *StrlenNode = std::get_if<size::variant::Strlen>(&size);
- // StrlenNode != nullptr) {
- // auto StrlenArgTypeWidth = Result.Context->getTypeSizeInChars(
- // StrlenNode->Arg->getType()->getPointeeType());
- // issueFixitIfWidthsMatch(
- // dst.Node, src.Node,
- // {StrlenArgTypeWidth, ExprAsString(CallNode.getArg(2))});
- // return;
- // }
- // if (const auto *SizeOfExprNode =
- // std::get_if<size::variant::SizeOfExpr>(&size);
- // SizeOfExprNode != nullptr) {
- // auto &Arg = SizeOfExprNode->Arg;
- // if (SizeOfExprNode->Arg->getType()->isArrayType()) {
- // issueFixitIfWidthsMatch(
- // dst.Node, src.Node,
- // {CharUnits::One(), ExprAsString(CallNode.getArg(2))});
- // return;
- // }
- // // __jm__ weird bc we assume dst and src are collections
- // // if Arg turns out to have the same type as dst or src then just
- // suggest
- // // copy via the assignment operator
- // if (auto argType = Arg->getType(); argType == DstVT or argType ==
- // SrcVT)
- // {
- // issueFixit{ValueTypeWidth, ExprAsString(CallNode.getArg(2))};
- // return;
- // }
- // // __jm__ only flag this as suspicious with a further explanation in
- // the
- // // diagnostic TODO: a sizeof of an unrelated type when copying between
- // // collections does not make a lot of sense
-
- // auto sizeDRE = [&]() -> const DeclRefExpr * {
- // if (const auto *Variant = std::get_if<size::variant::Strlen>(&size);
- // Variant != nullptr) {
- // return llvm::dyn_cast_if_present<DeclRefExpr>(Variant->Arg);
- // }
- // if (const auto *Variant =
- // std::get_if<size::variant::SizeOfExpr>(&size);
- // Variant != nullptr) {
- // return llvm::dyn_cast_if_present<DeclRefExpr>(Variant->Arg);
- // }
- // if (const auto *Variant =
- // std::get_if<size::variant::NSizeOfExpr>(&size);
- // Variant != nullptr) {
- // return llvm::dyn_cast_if_present<DeclRefExpr>(Variant->Arg);
- // }
- // return nullptr;
- // }();
-
- // one thing the analysis of the size argument must return is an Expr* Node
- // that we can lift into std::copy_n's third argument
-
- // 1. strlen(DeclRefExpr) also hints that the referenced variable is a
- // collection
-
- // 2. sizeof(expr)
- // 2.1. expr is a c-array DeclRefExpr to one of src/dst, copy_n size
- // becomes std::size(expr)
- // 2.2. both src and dst are not raw pointers and expr is a type of width
- // equal to vtw, essentialy fallthrough to 3
-
- // 3. N * sizeof(expr) is okay when expr is a type with width ==
- // ValueTypeWidth and N may be verbatim lifted into copy_n's third argument
-
- // to make sure we can issue a FixIt, need to be pretty sure we're dealing
- // with a collection and it is a byte collection
- // 1. For now we are sure both source and dest are collections, but not
- // necessarily of bytes
- // 2. We can relax conditions to where just one arg is a collection, and the
- // other can then be a collection or raw pointer. However, this is not
- // robust as there are cases where this may be used for unmarshalling
- // 3. when both dest and source are pointers (no expectations on the
- // argument as everything required is enforced by the type system) we can
- // use a heuristic using the form of the 3rd argument expression
- //
-
- // delete the memmove/memcpy
- // insert an std::copy_n
-
- // using PtrArgVariant = std::variant<CArrayTag, ContainerTag, RawPtrTag>;
- // struct PtrArg {
- // PtrArgVariant Tag;
- // const Expr *Node;
- // };
-
- // const auto MakePtrArg = [&](arg::tag::VariantPtrArgRef Refs) -> PtrArg {
- // if (const auto *Node = Result.Nodes.getNodeAs<Expr>(
- // arg::Dest::Refs.VariantRefs.AsCArray);
- // Node != nullptr) {
- // // freestanding std::begin
- // return {CArrayTag{}, Node};
- // }
- // if (const auto *Node = Result.Nodes.getNodeAs<Expr>(
- // arg::Dest::Refs.VariantRefs.AsContainer);
- // Node != nullptr) {
- // return {ContainerTag{}, Node};
- // }
- // const auto *Node = CallNode.getArg(Refs.FallbackParameterIdx);
- // return {RawPtrTag{}, Node};
- // };
-
- // auto Dest = MakePtrArg(arg::Dest::Refs.VariantRefs);
- // auto Source = MakePtrArg(arg::Source::Refs.VariantRefs);
-
- // if (std::holds_alternative<RawPtrTag>(Dest.Tag) and
- // std::holds_alternative<RawPtrTag>(Source.Tag) and
- // CheckSizeArgPermitsFix()) {
-
- // } else {
- // }
-
- {
- // 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());
- }
}
} // namespace modernize
>From 0abf669401c96cf4fdcfcc8890ddf0323a25de41 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Mon, 13 Jan 2025 23:25:41 +0100
Subject: [PATCH 12/15] Minor fixes [skip ci]
---
.../modernize/ReplaceWithStdCopyCheck.cpp | 66 ++++++++-----------
1 file changed, 29 insertions(+), 37 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
index a718937614d1a61..214183ccd0e49cd 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceWithStdCopyCheck.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "ReplaceWithStdCopyCheck.h"
-#include "ReplaceAutoPtrCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
@@ -44,15 +43,13 @@ struct Refs {
template <typename RefsT> auto createPtrArgMatcher() {
constexpr Refs Refs = RefsT::Refs;
- auto AllowedContainerNamesM = []() {
- // return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
- // "::std::vector", "::std::basic_string",
- // "::std::array");
- return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
- "::std::vector", "::std::basic_string", "::std::array",
- "::std::basic_string_view", "::std::span");
- }();
+ auto AllowedContainerNamesM =
+ hasAnyName("::std::vector", "::std::basic_string", "::std::array",
+ "::std::basic_string_view", "::std::span");
+ // Note: a problem with this matcher is that it automatically desugars the
+ // template argument type, so e.g. std::vector<std::int32_t> will bind
+ // ValueType to int and not std::int32_t
auto AllowedContainerTypeM = hasUnqualifiedDesugaredType(
recordType(hasDeclaration(recordDecl(classTemplateSpecializationDecl(
AllowedContainerNamesM,
@@ -71,8 +68,8 @@ template <typename RefsT> auto createPtrArgMatcher() {
auto StdDataReturnM =
returns(pointerType(pointee(qualType().bind(Refs.PtrCastFnReturnType))));
- auto StdDataMemberDeclM =
- cxxMethodDecl(hasName("data"), parameterCountIs(0), StdDataReturnM);
+ auto StdDataMemberDeclM = cxxMethodDecl(hasAnyName("data", "c_str"),
+ parameterCountIs(0), StdDataReturnM);
auto StdDataFreeDeclM = functionDecl(hasAnyName("::std::data", "::data"),
parameterCountIs(1), StdDataReturnM);
@@ -86,8 +83,6 @@ template <typename RefsT> auto createPtrArgMatcher() {
auto StdDataFreeCallM = callExpr(callee(StdDataFreeDeclM), argumentCountIs(1),
hasArgument(0, ArrayOrContainerM));
- // the last expr() in anyOf assumes previous matchers are ran eagerly from
- // left to right, still need to test this is the actual behaviour
return expr(anyOf(StdDataMemberCallM, StdDataFreeCallM, VariantCArrayM));
}
@@ -119,9 +114,11 @@ const QualType *extractValueType(const MatchFinder::MatchResult &Result) {
constexpr Refs Refs = RefT::Refs;
// checking equality is done here as opposed to when matching because the
- // equalsBoundNode matcher depends on the match order and the
+ // equalsBoundNode matcher depends on the match order.
+ // Already considered swapping the role of the node
+ // matchers, having one bind and the other match using equalsBoundNode, but
// PtrCastFnReturnType is only present in some scenarios,
- // making it tricky to swap the binding order
+ // so it's not applicable.
const auto *MaybeRetType =
Result.Nodes.getNodeAs<QualType>(Refs.PtrCastFnReturnType);
const auto *ValueType = Result.Nodes.getNodeAs<QualType>(Refs.ValueType);
@@ -244,7 +241,8 @@ auto createMatcher() {
SizeArg extractNode(const CallExpr &CallNode,
const MatchFinder::MatchResult &Result) {
- auto ConcreteCase = [&Nodes = Result.Nodes]() -> std::optional<SizeArg> {
+ auto TryExtractFromBoundTags =
+ [&Nodes = Result.Nodes]() -> std::optional<SizeArg> {
if (const auto *Array = Nodes.getNodeAs<Expr>(Refs.SizeOfCArray);
Array != nullptr)
return variant::SizeOfCArray{*Array};
@@ -267,7 +265,7 @@ SizeArg extractNode(const CallExpr &CallNode,
return std::nullopt;
};
- if (auto MaybeSize = ConcreteCase(); MaybeSize.has_value())
+ if (auto MaybeSize = TryExtractFromBoundTags(); MaybeSize.has_value())
return *MaybeSize;
return CallNode.getArg(ArgIndex);
}
@@ -299,12 +297,6 @@ void ReplaceWithStdCopyCheck::registerMatchers(MatchFinder *Finder) {
const auto OffendingDeclM =
functionDecl(parameterCountIs(3), createCalleeMatcher(FlagMemcpy));
- // cases:
- // 1. One of the arguments is definitely a collection and the other a pointer
- // - match
- // 2. Both source and dest are pointers, but size is of the form ((N :=
- // expr()) * sizeof(bytelike())) - match (false positive if N \in {0, 1})
-
const auto ExpressionM = callExpr(
callee(OffendingDeclM), optionally(ReturnValueUsedM),
allOf(optionally(hasArgument(dst::ArgIndex, dst::createMatcher())),
@@ -324,7 +316,6 @@ void ReplaceWithStdCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}
void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
- llvm::errs() << __LINE__ << '\n';
const auto &CallNode = *Result.Nodes.getNodeAs<CallExpr>(ExpressionRef);
auto Dst = dst::extractNode(CallNode, Result);
@@ -344,7 +335,6 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
*Result.SourceManager, getLangOpts())
.str();
};
- llvm::errs() << "Call: " << ExprAsString(CallNode) << '\n';
bool DstIsRawPtr = std::holds_alternative<ptrarg::tag::RawPtr>(Dst.Tag);
bool SrcIsRawPtr = std::holds_alternative<ptrarg::tag::RawPtr>(Src.Tag);
@@ -364,7 +354,8 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
auto Diag = diag(CallNode.getExprLoc(), "prefer std::copy_n to %0")
<< cast<NamedDecl>(CallNode.getCalleeDecl());
- // basis for converting size argument to std::copy_n's when issuing fixit
+ // the value type widths are helpful when translating the size argument
+ // from byte units (memcpy etc.) to element units (std::copy_n),
auto DstTypeWidth = Result.Context->getTypeSizeInChars(*DstVT);
auto SrcTypeWidth = Result.Context->getTypeSizeInChars(*SrcVT);
@@ -407,28 +398,27 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
if (not CheckIsFixable())
return;
- // From here we can assume dst and src have equal value type widths
+ assert(DstTypeWidth == SrcTypeWidth);
const auto &ValueTypeWidth = DstTypeWidth;
auto SrcFixit = [&]() {
auto AsString = ExprAsString(Src.Node);
if (SrcIsRawPtr)
return AsString;
- return "std::cbegin(" + AsString + ")";
+ return (llvm::Twine() + "std::cbegin(" + AsString + ")").str();
}();
auto DstFixit = [&]() {
auto AsString = ExprAsString(Dst.Node);
if (DstIsRawPtr)
return AsString;
- return "std::begin(" + AsString + ")";
+ return (llvm::Twine() + "std::begin(" + AsString + ")").str();
}();
- // this function is used to specify when a type from a sizeof(_) call is
+ // This function is used to specify when a type from a sizeof(_) call is
// considered equivalent to the value type of the collection.
- // for now it is relaxed because the template specialization matcher
- // seems to unqualify types automatically (e.g. the value type of
- // std::vector<std::int32_t> will just be int) and
+ // For now it is relaxed because the matcher desugars
+ // the container value types automatically.
auto CheckIsEquivValueType = [&DstVT](QualType SizeArgType) {
return SizeArgType->getCanonicalTypeUnqualified() ==
(*DstVT)->getCanonicalTypeUnqualified();
@@ -439,7 +429,9 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
std::get_if<size::variant::SizeOfCArray>(&Size);
SizeOfCArray != nullptr) {
// simply replaces sizeof(arr) with std::size(arr)
- return "std::size(" + ExprAsString(SizeOfCArray->Array) + ")";
+ return (llvm::Twine() + "std::size(" + ExprAsString(SizeOfCArray->Array) +
+ ")")
+ .str();
}
if (const auto *NSizeofExprNode =
std::get_if<size::variant::NSizeOfType>(&Size);
@@ -455,7 +447,7 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
SizeOfDivSizeOf != nullptr) {
auto &[Array, DivSizeOfType] = *SizeOfDivSizeOf;
if (CheckIsEquivValueType(DivSizeOfType) and ValueTypeWidth == CalleeUnit)
- return "std::size(" + ExprAsString(Array) + ")";
+ return (llvm::Twine() + "std::size(" + ExprAsString(Array) + ")").str();
}
// In the specific case where the collections' value types are byte-wide,
@@ -466,7 +458,7 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
// For all other cases where the value type is wider than one byte,
// and the size argument is of a form that is not easy to factor the unit
// out of, perform explicit division to ensure it is in element units
- return (llvm::Twine("") + "(" +
+ return (llvm::Twine() + "(" +
ExprAsString(*CallNode.getArg(size::ArgIndex)) + ") / sizeof(" +
DstVT->getAsString() + ")")
.str();
@@ -478,7 +470,7 @@ void ReplaceWithStdCopyCheck::check(const MatchFinder::MatchResult &Result) {
SizeOfDivSizeOf != nullptr) {
auto &[Array, DivSizeOfType] = *SizeOfDivSizeOf;
if (CheckIsEquivValueType(DivSizeOfType))
- return "std::size(" + ExprAsString(Array) + ")";
+ return (llvm::Twine() + "std::size(" + ExprAsString(Array) + ")").str();
}
// Since we assume wide variants' value type width equals wchar_t,
// the units should already be unified and no modifications to the size
>From f8dd28b4cb07204e7146c4686f86f7594e45005d Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Mon, 13 Jan 2025 23:25:55 +0100
Subject: [PATCH 13/15] Refine docs
---
.../modernize-replace-memcpy-with-stdcopy.rst | 47 ------------------
.../modernize/replace-with-std-copy.rst | 48 ++++++++-----------
2 files changed, 20 insertions(+), 75 deletions(-)
delete mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-replace-memcpy-with-stdcopy.rst
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
deleted file mode 100644
index 74276c37bc2c094..000000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/modernize-replace-memcpy-with-stdcopy.rst
+++ /dev/null
@@ -1,47 +0,0 @@
-.. title:: clang-tidy - modernize-with-stdcopy
-
-modernize-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-with-std-copy.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-with-std-copy.rst
index 7a28ab75ac2b926..4f3060112fa5de6 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-with-std-copy.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-with-std-copy.rst
@@ -1,48 +1,40 @@
-.. title:: clang-tidy - modernize-replace-with-stdcopy
+.. title:: clang-tidy - modernize-replace-with-std-copy
-modernize-replace-with-stdcopy
+modernize-replace-with-std-copy
===================================
-Replaces all occurrences of the C ``memmove`` function and its wide-char variant with ``std::copy_n``.
-Replacement of ``memcpy`` is optionally also supported.
+This check will flag all calls to ``memmove`` that can be possibly replaced with ``std::copy_n``.
+In some specific cases it will provide a fix automatically.
+``memcpy`` may also be flagged as opt-in. It is disabled by default because it performs no safety checks for overlapping ranges
+in the way ``memmove`` and ``std::copy_n`` do.
+``wmemmove`` and ``wmemcpy`` are also supported.
Example:
.. code-block:: c++
-
- /*!
- * \param dst Pointer to the destination array where the content is to be copied
- * \param src Pointer to the source of data to be copied
- * \param size Number of bytes to copy
- */
- memcpy(dst, src, size);
+ std::vector<int> dst(64);
+ memcpy(dst.data(), std::data(src), N);
becomes
.. code-block:: c++
+ std::vector<int> dst(64);
+ std::copy_n(std::cbegin(src), (N) / sizeof(int), std::begin(dst));
- /*!
- * \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_n(std::cbegin(src), size, std::begin(dst));
-
-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
+Known limitations
----------------
+For now, the check works only on a limited, recognizable subset of calls, where it can infer the arguments are pointers to valid collections
+in the sense that ``std::copy_n`` understands. More specifically, source/destination should be one of:
+- a call to ``std::data`` or the corresponding member method.
+- a fixed-size C-array.
-``std::copy_n`` is provided by the ``algorithm`` header file, this check will include it if needed.
+Moreover, a fix will not be issued in more complicated cases, e.g. when source and destination are collections of types that have different sizes.
Options
-------
.. option:: IncludeStyle
+ A string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.
- A string specifying which include-style is used, `llvm` or `google`. Default
- is `llvm`.
+.. option:: FlagMemcpy
+ A boolean specifying whether to flag calls to ``memcpy`` as well. Default is `false`.
\ No newline at end of file
>From e40000323e68c0859fc126f5c0a1d80b10e4e166 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Tue, 14 Jan 2025 17:42:38 +0100
Subject: [PATCH 14/15] Fix placement in release notes
---
clang-tools-extra/docs/ReleaseNotes.rst | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 94e1ea1a7238640..7e5ed2ea82f52f8 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -169,6 +169,11 @@ New checks
Finds cases when an uninstantiated virtual member function in a template class
causes cross-compiler incompatibility.
+- New :doc:`modernize-replace-with-std-copy
+ <clang-tidy/checks/modernize-replace-with-std-copy>` check.
+
+ Tries to replace calls to ``memmove`` and ``memcpy`` with an equivalent call to ``std::copy_n``.
+
New check aliases
^^^^^^^^^^^^^^^^^
@@ -319,11 +324,6 @@ Changes in existing checks
member function calls too and to only expand macros starting with ``PRI``
and ``__PRI`` from ``<inttypes.h>`` in the format string.
-- New :doc:`modernize-replace-with-stdcopy
- <clang-tidy/checks/modernize-replace-with-stdcopy>` check.
-
- Replaces all occurrences of the C ``memcpy`` function by ``std::copy``.
-
- Improved :doc:`modernize-use-std-print
<clang-tidy/checks/modernize/use-std-print>` check to support replacing
member function calls too and to only expand macros starting with ``PRI``
>From a308ba03e4582d0a04e6ec509ad26e7355832e60 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Sat, 8 Feb 2025 19:23:06 +0100
Subject: [PATCH 15/15] Update TidyFastChecks
---
clang-tools-extra/clangd/TidyFastChecks.inc | 765 ++++++++++----------
1 file changed, 386 insertions(+), 379 deletions(-)
diff --git a/clang-tools-extra/clangd/TidyFastChecks.inc b/clang-tools-extra/clangd/TidyFastChecks.inc
index a5d40a486976a3b..9076779cacc011a 100644
--- a/clang-tools-extra/clangd/TidyFastChecks.inc
+++ b/clang-tools-extra/clangd/TidyFastChecks.inc
@@ -7,437 +7,444 @@
#define SLOW(CHECK, DELTA)
#endif
-FAST(abseil-cleanup-ctad, -2.0)
-FAST(abseil-duration-addition, 0.0)
+FAST(abseil-cleanup-ctad, 3.0)
+FAST(abseil-duration-addition, 2.0)
FAST(abseil-duration-comparison, -1.0)
-FAST(abseil-duration-conversion-cast, -1.0)
-FAST(abseil-duration-division, 0.0)
-FAST(abseil-duration-factory-float, 2.0)
-FAST(abseil-duration-factory-scale, 1.0)
-FAST(abseil-duration-subtraction, -1.0)
-FAST(abseil-duration-unnecessary-conversion, -0.0)
-FAST(abseil-faster-strsplit-delimiter, 3.0)
-FAST(abseil-no-internal-dependencies, 1.0)
-FAST(abseil-no-namespace, -0.0)
+FAST(abseil-duration-conversion-cast, 1.0)
+FAST(abseil-duration-division, 4.0)
+FAST(abseil-duration-factory-float, -0.0)
+FAST(abseil-duration-factory-scale, -2.0)
+FAST(abseil-duration-subtraction, -0.0)
+FAST(abseil-duration-unnecessary-conversion, 2.0)
+FAST(abseil-faster-strsplit-delimiter, -3.0)
+FAST(abseil-no-internal-dependencies, -2.0)
+FAST(abseil-no-namespace, 2.0)
FAST(abseil-redundant-strcat-calls, 1.0)
-FAST(abseil-str-cat-append, -0.0)
-FAST(abseil-string-find-startswith, -1.0)
+FAST(abseil-str-cat-append, 1.0)
+FAST(abseil-string-find-startswith, -2.0)
FAST(abseil-string-find-str-contains, 4.0)
-FAST(abseil-time-comparison, -1.0)
-FAST(abseil-time-subtraction, 1.0)
-FAST(abseil-upgrade-duration-conversions, 2.0)
-SLOW(altera-id-dependent-backward-branch, 13.0)
-FAST(altera-kernel-name-restriction, 4.0)
-FAST(altera-single-work-item-barrier, 1.0)
-FAST(altera-struct-pack-align, -0.0)
-FAST(altera-unroll-loops, 2.0)
-FAST(android-cloexec-accept, 0.0)
-FAST(android-cloexec-accept4, 1.0)
-FAST(android-cloexec-creat, 1.0)
+FAST(abseil-time-comparison, 1.0)
+FAST(abseil-time-subtraction, 2.0)
+FAST(abseil-upgrade-duration-conversions, 1.0)
+SLOW(altera-id-dependent-backward-branch, 9.0)
+FAST(altera-kernel-name-restriction, -0.0)
+FAST(altera-single-work-item-barrier, -3.0)
+FAST(altera-struct-pack-align, 1.0)
+FAST(altera-unroll-loops, 3.0)
+FAST(android-cloexec-accept, 1.0)
+FAST(android-cloexec-accept4, 3.0)
+FAST(android-cloexec-creat, 4.0)
FAST(android-cloexec-dup, 0.0)
FAST(android-cloexec-epoll-create, 2.0)
-FAST(android-cloexec-epoll-create1, 0.0)
-FAST(android-cloexec-fopen, -1.0)
-FAST(android-cloexec-inotify-init, 2.0)
-FAST(android-cloexec-inotify-init1, -0.0)
-FAST(android-cloexec-memfd-create, -1.0)
-FAST(android-cloexec-open, 1.0)
-FAST(android-cloexec-pipe, -0.0)
+FAST(android-cloexec-epoll-create1, -1.0)
+FAST(android-cloexec-fopen, 1.0)
+FAST(android-cloexec-inotify-init, 0.0)
+FAST(android-cloexec-inotify-init1, -1.0)
+FAST(android-cloexec-memfd-create, -4.0)
+FAST(android-cloexec-open, -0.0)
+FAST(android-cloexec-pipe, 0.0)
FAST(android-cloexec-pipe2, 0.0)
-FAST(android-cloexec-socket, 1.0)
+FAST(android-cloexec-socket, -1.0)
FAST(android-comparison-in-temp-failure-retry, 1.0)
-FAST(boost-use-ranges, 2.0)
-FAST(boost-use-to-string, 2.0)
-FAST(bugprone-argument-comment, 4.0)
-FAST(bugprone-assert-side-effect, 1.0)
-FAST(bugprone-assignment-in-if-condition, 2.0)
-FAST(bugprone-bad-signal-to-kill-thread, 1.0)
-FAST(bugprone-bool-pointer-implicit-conversion, 0.0)
+FAST(boost-use-ranges, 3.0)
+FAST(boost-use-to-string, 0.0)
+FAST(bugprone-argument-comment, 2.0)
+FAST(bugprone-assert-side-effect, 2.0)
+FAST(bugprone-assignment-in-if-condition, -5.0)
+FAST(bugprone-bad-signal-to-kill-thread, -0.0)
+FAST(bugprone-bitwise-pointer-cast, -2.0)
+FAST(bugprone-bool-pointer-implicit-conversion, 4.0)
FAST(bugprone-branch-clone, 1.0)
-FAST(bugprone-casting-through-void, 1.0)
-FAST(bugprone-chained-comparison, 1.0)
-FAST(bugprone-compare-pointer-to-member-virtual-function, -0.0)
-FAST(bugprone-copy-constructor-init, 1.0)
-FAST(bugprone-crtp-constructor-accessibility, 0.0)
-FAST(bugprone-dangling-handle, -0.0)
-FAST(bugprone-dynamic-static-initializers, 0.0)
-FAST(bugprone-easily-swappable-parameters, 2.0)
-FAST(bugprone-empty-catch, 1.0)
-FAST(bugprone-exception-escape, 0.0)
-FAST(bugprone-fold-init-type, 1.0)
-FAST(bugprone-forward-declaration-namespace, 0.0)
-FAST(bugprone-forwarding-reference-overload, -1.0)
-FAST(bugprone-implicit-widening-of-multiplication-result, 2.0)
-FAST(bugprone-inaccurate-erase, -0.0)
-FAST(bugprone-inc-dec-in-conditions, 3.0)
-FAST(bugprone-incorrect-enable-if, -1.0)
-FAST(bugprone-incorrect-roundings, 1.0)
-FAST(bugprone-infinite-loop, 1.0)
-FAST(bugprone-integer-division, -0.0)
-FAST(bugprone-lambda-function-name, 0.0)
-FAST(bugprone-macro-parentheses, 1.0)
-FAST(bugprone-macro-repeated-side-effects, 1.0)
-FAST(bugprone-misplaced-operator-in-strlen-in-alloc, 0.0)
-FAST(bugprone-misplaced-pointer-arithmetic-in-alloc, -0.0)
-FAST(bugprone-misplaced-widening-cast, -1.0)
-FAST(bugprone-move-forwarding-reference, -1.0)
+FAST(bugprone-casting-through-void, -1.0)
+FAST(bugprone-chained-comparison, 2.0)
+FAST(bugprone-compare-pointer-to-member-virtual-function, -4.0)
+FAST(bugprone-copy-constructor-init, -0.0)
+FAST(bugprone-crtp-constructor-accessibility, 1.0)
+FAST(bugprone-dangling-handle, 1.0)
+FAST(bugprone-dynamic-static-initializers, 1.0)
+FAST(bugprone-easily-swappable-parameters, 1.0)
+FAST(bugprone-empty-catch, 3.0)
+FAST(bugprone-exception-escape, -0.0)
+FAST(bugprone-fold-init-type, -1.0)
+FAST(bugprone-forward-declaration-namespace, -1.0)
+FAST(bugprone-forwarding-reference-overload, -4.0)
+FAST(bugprone-implicit-widening-of-multiplication-result, 3.0)
+FAST(bugprone-inaccurate-erase, 1.0)
+FAST(bugprone-inc-dec-in-conditions, 5.0)
+FAST(bugprone-incorrect-enable-if, 2.0)
+FAST(bugprone-incorrect-roundings, 2.0)
+FAST(bugprone-infinite-loop, 7.0)
+FAST(bugprone-integer-division, 3.0)
+FAST(bugprone-lambda-function-name, 2.0)
+FAST(bugprone-macro-parentheses, 2.0)
+FAST(bugprone-macro-repeated-side-effects, 2.0)
+FAST(bugprone-misplaced-operator-in-strlen-in-alloc, 1.0)
+FAST(bugprone-misplaced-pointer-arithmetic-in-alloc, 2.0)
+FAST(bugprone-misplaced-widening-cast, 1.0)
+FAST(bugprone-move-forwarding-reference, -0.0)
FAST(bugprone-multi-level-implicit-pointer-conversion, -1.0)
-FAST(bugprone-multiple-new-in-one-expression, 0.0)
-FAST(bugprone-multiple-statement-macro, 2.0)
+FAST(bugprone-multiple-new-in-one-expression, -1.0)
+FAST(bugprone-multiple-statement-macro, -3.0)
FAST(bugprone-narrowing-conversions, 2.0)
-FAST(bugprone-no-escape, 1.0)
-FAST(bugprone-non-zero-enum-to-bool-conversion, 0.0)
-FAST(bugprone-not-null-terminated-result, 0.0)
-FAST(bugprone-optional-value-conversion, 1.0)
-FAST(bugprone-parent-virtual-call, 1.0)
-FAST(bugprone-pointer-arithmetic-on-polymorphic-object, 0.0)
-FAST(bugprone-posix-return, -0.0)
-FAST(bugprone-redundant-branch-condition, -0.0)
-FAST(bugprone-reserved-identifier, -1.0)
-FAST(bugprone-return-const-ref-from-parameter, -2.0)
-FAST(bugprone-shared-ptr-array-mismatch, 0.0)
-FAST(bugprone-signal-handler, -1.0)
-FAST(bugprone-signed-char-misuse, -2.0)
-FAST(bugprone-sizeof-container, -1.0)
-FAST(bugprone-sizeof-expression, 1.0)
-FAST(bugprone-spuriously-wake-up-functions, 1.0)
-FAST(bugprone-standalone-empty, 7.0)
-FAST(bugprone-string-constructor, 3.0)
-FAST(bugprone-string-integer-assignment, -0.0)
-FAST(bugprone-string-literal-with-embedded-nul, 1.0)
-FAST(bugprone-stringview-nullptr, 4.0)
+FAST(bugprone-no-escape, 2.0)
+FAST(bugprone-non-zero-enum-to-bool-conversion, 1.0)
+FAST(bugprone-nondeterministic-pointer-iteration-order, 2.0)
+FAST(bugprone-not-null-terminated-result, 1.0)
+FAST(bugprone-optional-value-conversion, 2.0)
+FAST(bugprone-parent-virtual-call, -2.0)
+FAST(bugprone-pointer-arithmetic-on-polymorphic-object, 1.0)
+FAST(bugprone-posix-return, 2.0)
+FAST(bugprone-redundant-branch-condition, 1.0)
+FAST(bugprone-reserved-identifier, 2.0)
+FAST(bugprone-return-const-ref-from-parameter, 2.0)
+FAST(bugprone-shared-ptr-array-mismatch, 3.0)
+FAST(bugprone-signal-handler, 2.0)
+FAST(bugprone-signed-char-misuse, -1.0)
+FAST(bugprone-sizeof-container, -2.0)
+FAST(bugprone-sizeof-expression, 6.0)
+FAST(bugprone-spuriously-wake-up-functions, 0.0)
+FAST(bugprone-standalone-empty, 5.0)
+FAST(bugprone-string-constructor, 0.0)
+FAST(bugprone-string-integer-assignment, 1.0)
+FAST(bugprone-string-literal-with-embedded-nul, 0.0)
+FAST(bugprone-stringview-nullptr, 2.0)
FAST(bugprone-suspicious-enum-usage, 2.0)
FAST(bugprone-suspicious-include, 0.0)
-FAST(bugprone-suspicious-memory-comparison, 0.0)
-FAST(bugprone-suspicious-memset-usage, 0.0)
-FAST(bugprone-suspicious-missing-comma, -2.0)
-FAST(bugprone-suspicious-realloc-usage, -0.0)
-FAST(bugprone-suspicious-semicolon, 6.0)
-FAST(bugprone-suspicious-string-compare, 1.0)
-FAST(bugprone-suspicious-stringview-data-usage, 1.0)
-FAST(bugprone-swapped-arguments, 1.0)
-FAST(bugprone-switch-missing-default-case, 2.0)
-FAST(bugprone-terminating-continue, -1.0)
-FAST(bugprone-throw-keyword-missing, 0.0)
-FAST(bugprone-too-small-loop-variable, 0.0)
+FAST(bugprone-suspicious-memory-comparison, -0.0)
+FAST(bugprone-suspicious-memset-usage, 1.0)
+FAST(bugprone-suspicious-missing-comma, -3.0)
+FAST(bugprone-suspicious-realloc-usage, 2.0)
+FAST(bugprone-suspicious-semicolon, 3.0)
+FAST(bugprone-suspicious-string-compare, 3.0)
+FAST(bugprone-suspicious-stringview-data-usage, -0.0)
+FAST(bugprone-swapped-arguments, 2.0)
+FAST(bugprone-switch-missing-default-case, 0.0)
+FAST(bugprone-tagged-union-member-count, -2.0)
+FAST(bugprone-terminating-continue, 3.0)
+FAST(bugprone-throw-keyword-missing, -1.0)
+FAST(bugprone-too-small-loop-variable, 3.0)
FAST(bugprone-unchecked-optional-access, 2.0)
FAST(bugprone-undefined-memory-manipulation, 1.0)
-FAST(bugprone-undelegated-constructor, 1.0)
-FAST(bugprone-unhandled-exception-at-new, -1.0)
-FAST(bugprone-unhandled-self-assignment, 0.0)
-FAST(bugprone-unique-ptr-array-mismatch, 0.0)
-FAST(bugprone-unsafe-functions, 1.0)
-FAST(bugprone-unused-local-non-trivial-variable, -1.0)
-FAST(bugprone-unused-raii, 1.0)
+FAST(bugprone-undelegated-constructor, 4.0)
+FAST(bugprone-unhandled-exception-at-new, 2.0)
+FAST(bugprone-unhandled-self-assignment, 4.0)
+FAST(bugprone-unique-ptr-array-mismatch, 5.0)
+FAST(bugprone-unsafe-functions, 0.0)
+FAST(bugprone-unused-local-non-trivial-variable, 2.0)
+FAST(bugprone-unused-raii, 3.0)
FAST(bugprone-unused-return-value, 4.0)
-FAST(bugprone-use-after-move, 4.0)
-FAST(bugprone-virtual-near-miss, 0.0)
-FAST(cert-con36-c, 1.0)
-FAST(cert-con54-cpp, 2.0)
-FAST(cert-ctr56-cpp, 0.0)
-FAST(cert-dcl03-c, 0.0)
-FAST(cert-dcl16-c, 1.0)
-FAST(cert-dcl37-c, 1.0)
-FAST(cert-dcl50-cpp, -1.0)
-FAST(cert-dcl51-cpp, -1.0)
-FAST(cert-dcl54-cpp, 0.0)
-FAST(cert-dcl58-cpp, -0.0)
-FAST(cert-dcl59-cpp, 1.0)
-FAST(cert-env33-c, 1.0)
-FAST(cert-err09-cpp, -0.0)
+FAST(bugprone-use-after-move, -0.0)
+FAST(bugprone-virtual-near-miss, -1.0)
+FAST(cert-arr39-c, 1.0)
+FAST(cert-con36-c, 2.0)
+FAST(cert-con54-cpp, 3.0)
+FAST(cert-ctr56-cpp, 4.0)
+FAST(cert-dcl03-c, 4.0)
+FAST(cert-dcl16-c, 2.0)
+FAST(cert-dcl37-c, 2.0)
+FAST(cert-dcl50-cpp, 1.0)
+FAST(cert-dcl51-cpp, 0.0)
+FAST(cert-dcl54-cpp, 3.0)
+FAST(cert-dcl58-cpp, -1.0)
+FAST(cert-dcl59-cpp, -4.0)
+FAST(cert-env33-c, 2.0)
+FAST(cert-err09-cpp, 2.0)
FAST(cert-err33-c, 4.0)
-FAST(cert-err34-c, -1.0)
-FAST(cert-err52-cpp, -1.0)
-FAST(cert-err58-cpp, -0.0)
-FAST(cert-err60-cpp, -0.0)
-FAST(cert-err61-cpp, 2.0)
-FAST(cert-exp42-c, 1.0)
-FAST(cert-fio38-c, 1.0)
-FAST(cert-flp30-c, 3.0)
-FAST(cert-flp37-c, 1.0)
-FAST(cert-int09-c, -1.0)
-FAST(cert-mem57-cpp, 0.0)
+FAST(cert-err34-c, 1.0)
+FAST(cert-err52-cpp, 0.0)
+FAST(cert-err58-cpp, 2.0)
+FAST(cert-err60-cpp, 1.0)
+FAST(cert-err61-cpp, 4.0)
+FAST(cert-exp42-c, 2.0)
+FAST(cert-fio38-c, -0.0)
+FAST(cert-flp30-c, 2.0)
+FAST(cert-flp37-c, 2.0)
+FAST(cert-int09-c, 2.0)
+FAST(cert-mem57-cpp, 2.0)
FAST(cert-msc24-c, 0.0)
-FAST(cert-msc30-c, 0.0)
-FAST(cert-msc32-c, -0.0)
+FAST(cert-msc30-c, 1.0)
+FAST(cert-msc32-c, -1.0)
FAST(cert-msc33-c, 2.0)
-FAST(cert-msc50-cpp, -0.0)
-FAST(cert-msc51-cpp, 2.0)
-FAST(cert-msc54-cpp, -0.0)
-FAST(cert-oop11-cpp, -0.0)
-FAST(cert-oop54-cpp, 2.0)
-FAST(cert-oop57-cpp, -0.0)
-FAST(cert-oop58-cpp, 0.0)
-FAST(cert-pos44-c, 2.0)
-FAST(cert-pos47-c, 0.0)
-FAST(cert-sig30-c, 1.0)
-FAST(cert-str34-c, 2.0)
-FAST(concurrency-mt-unsafe, 3.0)
-FAST(concurrency-thread-canceltype-asynchronous, 1.0)
-FAST(cppcoreguidelines-avoid-c-arrays, 0.0)
+FAST(cert-msc50-cpp, 3.0)
+FAST(cert-msc51-cpp, -0.0)
+FAST(cert-msc54-cpp, 0.0)
+FAST(cert-oop11-cpp, -3.0)
+FAST(cert-oop54-cpp, 1.0)
+FAST(cert-oop57-cpp, -1.0)
+FAST(cert-oop58-cpp, 8.0)
+FAST(cert-pos44-c, 1.0)
+FAST(cert-pos47-c, 4.0)
+FAST(cert-sig30-c, 3.0)
+FAST(cert-str34-c, 1.0)
+FAST(concurrency-mt-unsafe, -2.0)
+FAST(concurrency-thread-canceltype-asynchronous, -1.0)
+FAST(cppcoreguidelines-avoid-c-arrays, 2.0)
FAST(cppcoreguidelines-avoid-capturing-lambda-coroutines, -1.0)
-FAST(cppcoreguidelines-avoid-const-or-ref-data-members, -2.0)
-FAST(cppcoreguidelines-avoid-do-while, -1.0)
-FAST(cppcoreguidelines-avoid-goto, -1.0)
-FAST(cppcoreguidelines-avoid-magic-numbers, -2.0)
-FAST(cppcoreguidelines-avoid-non-const-global-variables, -0.0)
-FAST(cppcoreguidelines-avoid-reference-coroutine-parameters, -0.0)
-FAST(cppcoreguidelines-c-copy-assignment-signature, 1.0)
-FAST(cppcoreguidelines-explicit-virtual-functions, 0.0)
-FAST(cppcoreguidelines-init-variables, 1.0)
+FAST(cppcoreguidelines-avoid-const-or-ref-data-members, 1.0)
+FAST(cppcoreguidelines-avoid-do-while, 6.0)
+FAST(cppcoreguidelines-avoid-goto, -0.0)
+FAST(cppcoreguidelines-avoid-magic-numbers, 4.0)
+FAST(cppcoreguidelines-avoid-non-const-global-variables, -1.0)
+FAST(cppcoreguidelines-avoid-reference-coroutine-parameters, 1.0)
+FAST(cppcoreguidelines-c-copy-assignment-signature, -1.0)
+FAST(cppcoreguidelines-explicit-virtual-functions, -1.0)
+FAST(cppcoreguidelines-init-variables, 4.0)
FAST(cppcoreguidelines-interfaces-global-init, 1.0)
-FAST(cppcoreguidelines-macro-to-enum, 0.0)
-FAST(cppcoreguidelines-macro-usage, -0.0)
-FAST(cppcoreguidelines-misleading-capture-default-by-value, -1.0)
-FAST(cppcoreguidelines-missing-std-forward, 0.0)
-FAST(cppcoreguidelines-narrowing-conversions, 2.0)
-FAST(cppcoreguidelines-no-malloc, -1.0)
-FAST(cppcoreguidelines-no-suspend-with-lock, 1.0)
-FAST(cppcoreguidelines-noexcept-destructor, -0.0)
-FAST(cppcoreguidelines-noexcept-move-operations, 2.0)
-FAST(cppcoreguidelines-noexcept-swap, -2.0)
-FAST(cppcoreguidelines-non-private-member-variables-in-classes, 1.0)
-FAST(cppcoreguidelines-owning-memory, 3.0)
+FAST(cppcoreguidelines-macro-to-enum, 3.0)
+FAST(cppcoreguidelines-macro-usage, -2.0)
+FAST(cppcoreguidelines-misleading-capture-default-by-value, 1.0)
+FAST(cppcoreguidelines-missing-std-forward, 3.0)
+FAST(cppcoreguidelines-narrowing-conversions, 3.0)
+FAST(cppcoreguidelines-no-malloc, -2.0)
+FAST(cppcoreguidelines-no-suspend-with-lock, 2.0)
+FAST(cppcoreguidelines-noexcept-destructor, 3.0)
+FAST(cppcoreguidelines-noexcept-move-operations, 4.0)
+FAST(cppcoreguidelines-noexcept-swap, 0.0)
+FAST(cppcoreguidelines-non-private-member-variables-in-classes, 2.0)
+FAST(cppcoreguidelines-owning-memory, 2.0)
FAST(cppcoreguidelines-prefer-member-initializer, 2.0)
FAST(cppcoreguidelines-pro-bounds-array-to-pointer-decay, 2.0)
FAST(cppcoreguidelines-pro-bounds-constant-array-index, 1.0)
-FAST(cppcoreguidelines-pro-bounds-pointer-arithmetic, 0.0)
-FAST(cppcoreguidelines-pro-type-const-cast, -1.0)
-FAST(cppcoreguidelines-pro-type-cstyle-cast, 2.0)
-FAST(cppcoreguidelines-pro-type-member-init, 1.0)
-FAST(cppcoreguidelines-pro-type-reinterpret-cast, -1.0)
-FAST(cppcoreguidelines-pro-type-static-cast-downcast, 0.0)
-FAST(cppcoreguidelines-pro-type-union-access, 0.0)
-FAST(cppcoreguidelines-pro-type-vararg, -1.0)
-FAST(cppcoreguidelines-rvalue-reference-param-not-moved, 1.0)
-FAST(cppcoreguidelines-slicing, 1.0)
-FAST(cppcoreguidelines-special-member-functions, -1.0)
-FAST(cppcoreguidelines-use-default-member-init, 0.0)
-FAST(cppcoreguidelines-virtual-class-destructor, -0.0)
-FAST(darwin-avoid-spinlock, 2.0)
-FAST(darwin-dispatch-once-nonstatic, 0.0)
-FAST(fuchsia-default-arguments-calls, 1.0)
-FAST(fuchsia-default-arguments-declarations, -0.0)
-FAST(fuchsia-header-anon-namespaces, -0.0)
-FAST(fuchsia-multiple-inheritance, 0.0)
-FAST(fuchsia-overloaded-operator, 4.0)
-FAST(fuchsia-statically-constructed-objects, -0.0)
-FAST(fuchsia-trailing-return, 1.0)
-FAST(fuchsia-virtual-inheritance, 1.0)
-FAST(google-build-explicit-make-pair, 3.0)
+FAST(cppcoreguidelines-pro-bounds-pointer-arithmetic, 1.0)
+FAST(cppcoreguidelines-pro-type-const-cast, 1.0)
+FAST(cppcoreguidelines-pro-type-cstyle-cast, -3.0)
+FAST(cppcoreguidelines-pro-type-member-init, 5.0)
+FAST(cppcoreguidelines-pro-type-reinterpret-cast, 1.0)
+FAST(cppcoreguidelines-pro-type-static-cast-downcast, 2.0)
+FAST(cppcoreguidelines-pro-type-union-access, 2.0)
+FAST(cppcoreguidelines-pro-type-vararg, -0.0)
+FAST(cppcoreguidelines-rvalue-reference-param-not-moved, 0.0)
+FAST(cppcoreguidelines-slicing, -2.0)
+FAST(cppcoreguidelines-special-member-functions, 1.0)
+FAST(cppcoreguidelines-use-default-member-init, 1.0)
+FAST(cppcoreguidelines-virtual-class-destructor, 2.0)
+FAST(darwin-avoid-spinlock, 5.0)
+FAST(darwin-dispatch-once-nonstatic, 3.0)
+FAST(fuchsia-default-arguments-calls, 0.0)
+FAST(fuchsia-default-arguments-declarations, 3.0)
+FAST(fuchsia-header-anon-namespaces, 6.0)
+FAST(fuchsia-multiple-inheritance, 2.0)
+FAST(fuchsia-overloaded-operator, 3.0)
+FAST(fuchsia-statically-constructed-objects, 1.0)
+FAST(fuchsia-trailing-return, -0.0)
+FAST(fuchsia-virtual-inheritance, 0.0)
+FAST(google-build-explicit-make-pair, 2.0)
FAST(google-build-namespaces, -1.0)
-FAST(google-build-using-namespace, -0.0)
-FAST(google-default-arguments, 0.0)
-FAST(google-explicit-constructor, 2.0)
-FAST(google-global-names-in-headers, 0.0)
-FAST(google-objc-avoid-nsobject-new, 1.0)
-FAST(google-objc-avoid-throwing-exception, -0.0)
-FAST(google-objc-function-naming, -1.0)
-FAST(google-objc-global-variable-declaration, 0.0)
+FAST(google-build-using-namespace, 1.0)
+FAST(google-default-arguments, -0.0)
+FAST(google-explicit-constructor, 0.0)
+FAST(google-global-names-in-headers, -0.0)
+FAST(google-objc-avoid-nsobject-new, -0.0)
+FAST(google-objc-avoid-throwing-exception, -3.0)
+FAST(google-objc-function-naming, -2.0)
+FAST(google-objc-global-variable-declaration, -1.0)
FAST(google-readability-avoid-underscore-in-googletest-name, 1.0)
-FAST(google-readability-braces-around-statements, 0.0)
-FAST(google-readability-casting, -0.0)
-FAST(google-readability-function-size, 3.0)
-FAST(google-readability-namespace-comments, -0.0)
-FAST(google-readability-todo, 1.0)
-FAST(google-runtime-int, 0.0)
-FAST(google-runtime-operator, 0.0)
-FAST(google-upgrade-googletest-case, 1.0)
-FAST(hicpp-avoid-c-arrays, 1.0)
-FAST(hicpp-avoid-goto, -0.0)
-FAST(hicpp-braces-around-statements, 0.0)
-FAST(hicpp-deprecated-headers, 1.0)
-FAST(hicpp-exception-baseclass, -1.0)
-FAST(hicpp-explicit-conversions, 1.0)
-FAST(hicpp-function-size, 1.0)
-FAST(hicpp-ignored-remove-result, 3.0)
+FAST(google-readability-braces-around-statements, -3.0)
+FAST(google-readability-casting, 2.0)
+FAST(google-readability-function-size, 4.0)
+FAST(google-readability-namespace-comments, 2.0)
+FAST(google-readability-todo, 2.0)
+FAST(google-runtime-int, 2.0)
+FAST(google-runtime-operator, -6.0)
+FAST(google-upgrade-googletest-case, -0.0)
+FAST(hicpp-avoid-c-arrays, -1.0)
+FAST(hicpp-avoid-goto, -1.0)
+FAST(hicpp-braces-around-statements, -0.0)
+FAST(hicpp-deprecated-headers, -1.0)
+FAST(hicpp-exception-baseclass, 1.0)
+FAST(hicpp-explicit-conversions, 2.0)
+FAST(hicpp-function-size, 2.0)
+FAST(hicpp-ignored-remove-result, 2.0)
FAST(hicpp-invalid-access-moved, 4.0)
FAST(hicpp-member-init, 2.0)
-FAST(hicpp-move-const-arg, 3.0)
-FAST(hicpp-multiway-paths-covered, 0.0)
-FAST(hicpp-named-parameter, 2.0)
-FAST(hicpp-new-delete-operators, -0.0)
-FAST(hicpp-no-array-decay, 4.0)
+FAST(hicpp-move-const-arg, 1.0)
+FAST(hicpp-multiway-paths-covered, -0.0)
+FAST(hicpp-named-parameter, 1.0)
+FAST(hicpp-new-delete-operators, 2.0)
+FAST(hicpp-no-array-decay, 2.0)
FAST(hicpp-no-assembler, 1.0)
FAST(hicpp-no-malloc, 2.0)
-FAST(hicpp-noexcept-move, -0.0)
-FAST(hicpp-signed-bitwise, -1.0)
-FAST(hicpp-special-member-functions, -2.0)
-FAST(hicpp-static-assert, 4.0)
-FAST(hicpp-undelegated-constructor, 6.0)
-FAST(hicpp-uppercase-literal-suffix, 5.0)
+FAST(hicpp-noexcept-move, 1.0)
+FAST(hicpp-signed-bitwise, 5.0)
+FAST(hicpp-special-member-functions, 0.0)
+FAST(hicpp-static-assert, 5.0)
+FAST(hicpp-undelegated-constructor, -1.0)
+FAST(hicpp-uppercase-literal-suffix, 2.0)
FAST(hicpp-use-auto, 0.0)
-FAST(hicpp-use-emplace, 3.0)
+FAST(hicpp-use-emplace, -0.0)
FAST(hicpp-use-equals-default, 2.0)
-FAST(hicpp-use-equals-delete, 1.0)
-FAST(hicpp-use-noexcept, -0.0)
-FAST(hicpp-use-nullptr, 1.0)
-FAST(hicpp-use-override, -1.0)
-FAST(hicpp-vararg, 0.0)
-FAST(linuxkernel-must-check-errs, -0.0)
-FAST(llvm-else-after-return, -1.0)
-FAST(llvm-header-guard, 3.0)
-FAST(llvm-include-order, 0.0)
-FAST(llvm-namespace-comment, 0.0)
-FAST(llvm-prefer-isa-or-dyn-cast-in-conditionals, 3.0)
-FAST(llvm-prefer-register-over-unsigned, -0.0)
-FAST(llvm-qualified-auto, 4.0)
-FAST(llvm-twine-local, -0.0)
-FAST(llvmlibc-callee-namespace, -0.0)
-FAST(llvmlibc-implementation-in-namespace, 1.0)
-FAST(llvmlibc-inline-function-decl, 3.0)
+FAST(hicpp-use-equals-delete, 0.0)
+FAST(hicpp-use-noexcept, -1.0)
+FAST(hicpp-use-nullptr, 2.0)
+FAST(hicpp-use-override, 1.0)
+FAST(hicpp-vararg, -3.0)
+FAST(linuxkernel-must-check-errs, 0.0)
+FAST(llvm-else-after-return, 1.0)
+FAST(llvm-header-guard, 2.0)
+FAST(llvm-include-order, 1.0)
+FAST(llvm-namespace-comment, 1.0)
+FAST(llvm-prefer-isa-or-dyn-cast-in-conditionals, 1.0)
+FAST(llvm-prefer-register-over-unsigned, -4.0)
+FAST(llvm-qualified-auto, -1.0)
+FAST(llvm-twine-local, -2.0)
+FAST(llvmlibc-callee-namespace, 0.0)
+FAST(llvmlibc-implementation-in-namespace, -0.0)
+FAST(llvmlibc-inline-function-decl, 1.0)
FAST(llvmlibc-restrict-system-libc-headers, 0.0)
-FAST(misc-confusable-identifiers, -1.0)
-SLOW(misc-const-correctness, 67.0)
-FAST(misc-coroutine-hostile-raii, 1.0)
+FAST(misc-confusable-identifiers, 1.0)
+FAST(misc-const-correctness, 5.0)
+FAST(misc-coroutine-hostile-raii, -2.0)
FAST(misc-definitions-in-headers, -1.0)
-SLOW(misc-header-include-cycle, 10.0)
-FAST(misc-include-cleaner, 5.0)
-FAST(misc-misleading-bidirectional, 1.0)
-FAST(misc-misleading-identifier, 3.0)
-FAST(misc-misplaced-const, -2.0)
-FAST(misc-new-delete-overloads, 1.0)
-FAST(misc-no-recursion, 0.0)
-FAST(misc-non-copyable-objects, 0.0)
-FAST(misc-non-private-member-variables-in-classes, -1.0)
-FAST(misc-redundant-expression, 1.0)
-FAST(misc-static-assert, 3.0)
-FAST(misc-throw-by-value-catch-by-reference, -0.0)
+FAST(misc-header-include-cycle, 2.0)
+FAST(misc-include-cleaner, 15.0)
+FAST(misc-misleading-bidirectional, 0.0)
+FAST(misc-misleading-identifier, 1.0)
+FAST(misc-misplaced-const, -1.0)
+FAST(misc-new-delete-overloads, 2.0)
+FAST(misc-no-recursion, 1.0)
+FAST(misc-non-copyable-objects, 2.0)
+FAST(misc-non-private-member-variables-in-classes, -0.0)
+FAST(misc-redundant-expression, 2.0)
+FAST(misc-static-assert, 2.0)
+FAST(misc-throw-by-value-catch-by-reference, 0.0)
FAST(misc-unconventional-assign-operator, 1.0)
-FAST(misc-uniqueptr-reset-release, 2.0)
-FAST(misc-unused-alias-decls, 2.0)
-FAST(misc-unused-parameters, 3.0)
-FAST(misc-unused-using-decls, 1.0)
-FAST(misc-use-anonymous-namespace, 1.0)
-FAST(misc-use-internal-linkage, 1.0)
-FAST(modernize-avoid-bind, 1.0)
-FAST(modernize-avoid-c-arrays, 2.0)
-FAST(modernize-concat-nested-namespaces, -0.0)
+FAST(misc-uniqueptr-reset-release, 1.0)
+FAST(misc-unused-alias-decls, 3.0)
+FAST(misc-unused-parameters, 2.0)
+FAST(misc-unused-using-decls, 0.0)
+FAST(misc-use-anonymous-namespace, 4.0)
+FAST(misc-use-internal-linkage, 3.0)
+FAST(modernize-avoid-bind, -2.0)
+FAST(modernize-avoid-c-arrays, 3.0)
+FAST(modernize-concat-nested-namespaces, 1.0)
FAST(modernize-deprecated-headers, 0.0)
FAST(modernize-deprecated-ios-base-aliases, 0.0)
-FAST(modernize-loop-convert, 2.0)
+FAST(modernize-loop-convert, 4.0)
FAST(modernize-macro-to-enum, 0.0)
-FAST(modernize-make-shared, 2.0)
+FAST(modernize-make-shared, -2.0)
FAST(modernize-make-unique, 1.0)
-FAST(modernize-min-max-use-initializer-list, 1.0)
-FAST(modernize-pass-by-value, 0.0)
-FAST(modernize-raw-string-literal, 2.0)
-FAST(modernize-redundant-void-arg, 1.0)
-FAST(modernize-replace-auto-ptr, 0.0)
-FAST(modernize-replace-disallow-copy-and-assign-macro, -0.0)
-FAST(modernize-replace-random-shuffle, 1.0)
+FAST(modernize-min-max-use-initializer-list, -1.0)
+FAST(modernize-pass-by-value, -1.0)
+FAST(modernize-raw-string-literal, 1.0)
+FAST(modernize-redundant-void-arg, -2.0)
+FAST(modernize-replace-auto-ptr, 1.0)
+FAST(modernize-replace-disallow-copy-and-assign-macro, -1.0)
+FAST(modernize-replace-random-shuffle, 0.0)
FAST(modernize-replace-with-std-copy, 1.0)
-FAST(modernize-return-braced-init-list, 1.0)
-FAST(modernize-shrink-to-fit, 1.0)
-FAST(modernize-type-traits, 1.0)
+FAST(modernize-return-braced-init-list, -1.0)
+FAST(modernize-shrink-to-fit, -2.0)
+FAST(modernize-type-traits, 3.0)
FAST(modernize-unary-static-assert, 1.0)
-FAST(modernize-use-auto, 0.0)
-FAST(modernize-use-bool-literals, 1.0)
-FAST(modernize-use-constraints, 1.0)
-FAST(modernize-use-default-member-init, -0.0)
+FAST(modernize-use-auto, -2.0)
+FAST(modernize-use-bool-literals, -2.0)
+FAST(modernize-use-constraints, -0.0)
+FAST(modernize-use-default-member-init, 2.0)
FAST(modernize-use-designated-initializers, 1.0)
-FAST(modernize-use-emplace, 2.0)
+FAST(modernize-use-emplace, 1.0)
FAST(modernize-use-equals-default, 1.0)
-FAST(modernize-use-equals-delete, 2.0)
-FAST(modernize-use-nodiscard, -2.0)
-FAST(modernize-use-noexcept, -2.0)
-FAST(modernize-use-nullptr, 1.0)
-FAST(modernize-use-override, 0.0)
-FAST(modernize-use-ranges, 0.0)
-FAST(modernize-use-starts-ends-with, 0.0)
-FAST(modernize-use-std-format, -1.0)
-FAST(modernize-use-std-numbers, 0.0)
-FAST(modernize-use-std-print, -0.0)
-FAST(modernize-use-trailing-return-type, 3.0)
-FAST(modernize-use-transparent-functors, 0.0)
-FAST(modernize-use-uncaught-exceptions, 0.0)
-FAST(modernize-use-using, 1.0)
-FAST(objc-assert-equals, 1.0)
-FAST(objc-avoid-nserror-init, -0.0)
-FAST(objc-dealloc-in-category, 0.0)
-FAST(objc-forbidden-subclassing, 2.0)
-FAST(objc-missing-hash, -0.0)
-FAST(objc-nsdate-formatter, 0.0)
-FAST(objc-nsinvocation-argument-lifetime, 0.0)
-FAST(objc-property-declaration, -0.0)
-FAST(objc-super-self, -2.0)
-FAST(openmp-exception-escape, -1.0)
-FAST(openmp-use-default-none, 2.0)
-FAST(performance-avoid-endl, 2.0)
-FAST(performance-enum-size, -1.0)
-FAST(performance-faster-string-find, 1.0)
-FAST(performance-for-range-copy, 1.0)
-FAST(performance-implicit-conversion-in-loop, 0.0)
-FAST(performance-inefficient-algorithm, 1.0)
-FAST(performance-inefficient-string-concatenation, 1.0)
-FAST(performance-inefficient-vector-operation, -0.0)
-FAST(performance-move-const-arg, 2.0)
-FAST(performance-move-constructor-init, 2.0)
-FAST(performance-no-automatic-move, 2.0)
-FAST(performance-no-int-to-ptr, 0.0)
-FAST(performance-noexcept-destructor, -2.0)
+FAST(modernize-use-equals-delete, -1.0)
+FAST(modernize-use-integer-sign-comparison, 1.0)
+FAST(modernize-use-nodiscard, -0.0)
+FAST(modernize-use-noexcept, 0.0)
+FAST(modernize-use-nullptr, 2.0)
+FAST(modernize-use-override, -1.0)
+FAST(modernize-use-ranges, -2.0)
+FAST(modernize-use-starts-ends-with, 2.0)
+FAST(modernize-use-std-format, -3.0)
+FAST(modernize-use-std-numbers, 1.0)
+FAST(modernize-use-std-print, -2.0)
+FAST(modernize-use-trailing-return-type, -0.0)
+FAST(modernize-use-transparent-functors, 1.0)
+FAST(modernize-use-uncaught-exceptions, -3.0)
+FAST(modernize-use-using, -1.0)
+FAST(objc-assert-equals, 2.0)
+FAST(objc-avoid-nserror-init, 1.0)
+FAST(objc-dealloc-in-category, -1.0)
+FAST(objc-forbidden-subclassing, 1.0)
+FAST(objc-missing-hash, 3.0)
+FAST(objc-nsdate-formatter, -1.0)
+FAST(objc-nsinvocation-argument-lifetime, -0.0)
+FAST(objc-property-declaration, 0.0)
+FAST(objc-super-self, -1.0)
+FAST(openmp-exception-escape, 1.0)
+FAST(openmp-use-default-none, -1.0)
+FAST(performance-avoid-endl, 3.0)
+FAST(performance-enum-size, 1.0)
+FAST(performance-faster-string-find, -1.0)
+FAST(performance-for-range-copy, 3.0)
+FAST(performance-implicit-conversion-in-loop, 1.0)
+FAST(performance-inefficient-algorithm, 0.0)
+FAST(performance-inefficient-string-concatenation, -2.0)
+FAST(performance-inefficient-vector-operation, -1.0)
+FAST(performance-move-const-arg, -2.0)
+FAST(performance-move-constructor-init, 1.0)
+FAST(performance-no-automatic-move, 0.0)
+FAST(performance-no-int-to-ptr, -2.0)
+FAST(performance-noexcept-destructor, 1.0)
FAST(performance-noexcept-move-constructor, 1.0)
-FAST(performance-noexcept-swap, -2.0)
-FAST(performance-trivially-destructible, 3.0)
-FAST(performance-type-promotion-in-math-fn, 2.0)
-FAST(performance-unnecessary-copy-initialization, 2.0)
-FAST(performance-unnecessary-value-param, 2.0)
-FAST(portability-restrict-system-includes, 1.0)
+FAST(performance-noexcept-swap, -3.0)
+FAST(performance-trivially-destructible, -1.0)
+FAST(performance-type-promotion-in-math-fn, 3.0)
+FAST(performance-unnecessary-copy-initialization, 5.0)
+FAST(performance-unnecessary-value-param, 0.0)
+FAST(portability-restrict-system-includes, 0.0)
FAST(portability-simd-intrinsics, 1.0)
-FAST(portability-std-allocator-const, 3.0)
-FAST(readability-avoid-const-params-in-decls, -0.0)
-FAST(readability-avoid-nested-conditional-operator, -1.0)
-FAST(readability-avoid-return-with-void-value, 0.0)
-FAST(readability-avoid-unconditional-preprocessor-if, -1.0)
-FAST(readability-braces-around-statements, 1.0)
-FAST(readability-const-return-type, -1.0)
-FAST(readability-container-contains, 3.0)
-FAST(readability-container-data-pointer, -1.0)
-SLOW(readability-container-size-empty, 13.0)
-FAST(readability-convert-member-functions-to-static, 4.0)
-FAST(readability-delete-null-pointer, -1.0)
-FAST(readability-duplicate-include, 2.0)
-FAST(readability-else-after-return, 0.0)
-FAST(readability-enum-initial-value, 0.0)
-FAST(readability-function-cognitive-complexity, 0.0)
-FAST(readability-function-size, 0.0)
+FAST(portability-std-allocator-const, 1.0)
+FAST(portability-template-virtual-member-function, 3.0)
+FAST(readability-avoid-const-params-in-decls, 2.0)
+FAST(readability-avoid-nested-conditional-operator, 1.0)
+FAST(readability-avoid-return-with-void-value, -1.0)
+FAST(readability-avoid-unconditional-preprocessor-if, 4.0)
+FAST(readability-braces-around-statements, -1.0)
+FAST(readability-const-return-type, 0.0)
+FAST(readability-container-contains, 9.0)
+FAST(readability-container-data-pointer, 1.0)
+FAST(readability-container-size-empty, 1.0)
+FAST(readability-convert-member-functions-to-static, 1.0)
+FAST(readability-delete-null-pointer, 0.0)
+FAST(readability-duplicate-include, -0.0)
+FAST(readability-else-after-return, 2.0)
+FAST(readability-enum-initial-value, 4.0)
+FAST(readability-function-cognitive-complexity, 3.0)
+FAST(readability-function-size, -3.0)
FAST(readability-identifier-length, 2.0)
-FAST(readability-identifier-naming, 1.0)
-FAST(readability-implicit-bool-conversion, 3.0)
-FAST(readability-inconsistent-declaration-parameter-name, -0.0)
-FAST(readability-isolate-declaration, 0.0)
-FAST(readability-magic-numbers, 4.0)
+FAST(readability-identifier-naming, 2.0)
+FAST(readability-implicit-bool-conversion, 1.0)
+FAST(readability-inconsistent-declaration-parameter-name, 2.0)
+FAST(readability-isolate-declaration, -1.0)
+FAST(readability-magic-numbers, -1.0)
FAST(readability-make-member-function-const, 1.0)
-FAST(readability-math-missing-parentheses, 1.0)
+FAST(readability-math-missing-parentheses, 2.0)
FAST(readability-misleading-indentation, 1.0)
-FAST(readability-misplaced-array-index, 0.0)
-FAST(readability-named-parameter, -0.0)
+FAST(readability-misplaced-array-index, -1.0)
+FAST(readability-named-parameter, -1.0)
FAST(readability-non-const-parameter, 2.0)
-FAST(readability-operators-representation, 0.0)
-FAST(readability-qualified-auto, 0.0)
-FAST(readability-redundant-access-specifiers, 1.0)
-FAST(readability-redundant-casting, -0.0)
+FAST(readability-operators-representation, -2.0)
+FAST(readability-qualified-auto, 1.0)
+FAST(readability-redundant-access-specifiers, -1.0)
+FAST(readability-redundant-casting, -1.0)
FAST(readability-redundant-control-flow, 1.0)
-FAST(readability-redundant-declaration, 1.0)
+FAST(readability-redundant-declaration, -2.0)
FAST(readability-redundant-function-ptr-dereference, 0.0)
-FAST(readability-redundant-inline-specifier, 0.0)
+FAST(readability-redundant-inline-specifier, -0.0)
FAST(readability-redundant-member-init, 1.0)
FAST(readability-redundant-preprocessor, -0.0)
-FAST(readability-redundant-smartptr-get, 4.0)
-FAST(readability-redundant-string-cstr, 1.0)
-FAST(readability-redundant-string-init, -0.0)
-FAST(readability-reference-to-constructed-temporary, 3.0)
-FAST(readability-simplify-boolean-expr, -0.0)
-FAST(readability-simplify-subscript-expr, 1.0)
-FAST(readability-static-accessed-through-instance, 0.0)
-FAST(readability-static-definition-in-anonymous-namespace, 0.0)
-FAST(readability-string-compare, -0.0)
-FAST(readability-suspicious-call-argument, -1.0)
-FAST(readability-uniqueptr-delete-release, 3.0)
-FAST(readability-uppercase-literal-suffix, 0.0)
-FAST(readability-use-anyofallof, 2.0)
-FAST(readability-use-std-min-max, -1.0)
-FAST(zircon-temporary-objects, 1.0)
+FAST(readability-redundant-smartptr-get, -1.0)
+FAST(readability-redundant-string-cstr, -1.0)
+FAST(readability-redundant-string-init, 1.0)
+FAST(readability-reference-to-constructed-temporary, -0.0)
+FAST(readability-simplify-boolean-expr, 2.0)
+FAST(readability-simplify-subscript-expr, 0.0)
+FAST(readability-static-accessed-through-instance, 1.0)
+FAST(readability-static-definition-in-anonymous-namespace, 1.0)
+FAST(readability-string-compare, 1.0)
+FAST(readability-suspicious-call-argument, 1.0)
+FAST(readability-uniqueptr-delete-release, -0.0)
+FAST(readability-uppercase-literal-suffix, -3.0)
+FAST(readability-use-anyofallof, -1.0)
+FAST(readability-use-std-min-max, 2.0)
+FAST(zircon-temporary-objects, 0.0)
#undef FAST
#undef SLOW
+
More information about the cfe-commits
mailing list