[clang-tools-extra] [DRAFT][clang-tidy] modernize-replace-with-stdcopy (PR #113046)
Kuba MigdaĆ via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 19 08:09:58 PDT 2024
https://github.com/kidq330 created https://github.com/llvm/llvm-project/pull/113046
None
>From b2753fc81792cf5c3a95eedbf349bac2e461e883 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 1/3] 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 c919d49b42873a..83ebd2f12775e7 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 18607593320635..257c9373444761 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -24,6 +24,7 @@
#include "RedundantVoidArgCheck.h"
#include "ReplaceAutoPtrCheck.h"
#include "ReplaceDisallowCopyAndAssignMacroCheck.h"
+#include "ReplaceMemcpyWithStdCopy.h"
#include "ReplaceRandomShuffleCheck.h"
#include "ReturnBracedInitListCheck.h"
#include "ShrinkToFitCheck.h"
@@ -91,6 +92,8 @@ class ModernizeModule : public ClangTidyModule {
"modernize-replace-auto-ptr");
CheckFactories.registerCheck<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 00000000000000..af6b365c162517
--- /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 00000000000000..0f262bf839af24
--- /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 e8148e06b6af28..2d14f5ec913239 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -217,6 +217,11 @@ Changes in existing checks
member function calls too and to only expand macros starting with ``PRI``
and ``__PRI`` from ``<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 0082234f5ed31b..de8197f20b39a4 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -286,6 +286,7 @@ Clang-Tidy Checks
:doc:`modernize-raw-string-literal <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 00000000000000..922a7f36e7e078
--- /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 3c441775ba1b544c61355c4c2de28c1a00355154 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 2/3] 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 00000000000000..ce07fc5801da6a
--- /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 e83d86fefcb434abb08169f62e2e9842f7ea4f46 Mon Sep 17 00:00:00 2001
From: kidp330 <j.migdal42 at gmail.com>
Date: Sat, 19 Oct 2024 17:06:47 +0200
Subject: [PATCH 3/3] WIP update modernize-replace-memcpy-with-stdcopy
implementation
---
.../modernize/ModernizeTidyModule.cpp | 2 +-
.../modernize/ReplaceMemcpyWithStdCopy.cpp | 316 ++++++++++++++----
.../modernize/ReplaceMemcpyWithStdCopy.h | 31 +-
clang-tools-extra/docs/ReleaseNotes.rst | 4 +-
.../docs/clang-tidy/checks/list.rst | 2 +-
.../modernize/replace-memcpy-with-stdcopy.rst | 47 +++
6 files changed, 324 insertions(+), 78 deletions(-)
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/replace-memcpy-with-stdcopy.rst
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 257c9373444761..b36248ce1411d8 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -93,7 +93,7 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<ReplaceDisallowCopyAndAssignMacroCheck>(
"modernize-replace-disallow-copy-and-assign-macro");
CheckFactories.registerCheck<ReplaceMemcpyWithStdCopy>(
- "modernize-replace-memcpy-by-stdcopy");
+ "modernize-replace-memcpy-with-std-copy");
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
index af6b365c162517..eb7b89e89f5a36 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.cpp
@@ -7,113 +7,307 @@
//===----------------------------------------------------------------------===//
#include "ReplaceMemcpyWithStdCopy.h"
-#include "../utils/OptionsUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Tooling/FixIt.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorHandling.h"
#include <array>
+#include <optional>
+#include <type_traits>
+#include <variant>
using namespace clang;
using namespace clang::ast_matchers;
-namespace clang {
-namespace tidy {
-namespace modernize {
+namespace clang::tidy::modernize {
+static constexpr llvm::StringLiteral MemcpyRef = "::std::memcpy";
+
+namespace {
+// Helper Matcher which applies the given QualType Matcher either directly or by
+// resolving a pointer type to its pointee. Used to match v.push_back() as well
+// as p->push_back().
+auto hasTypeOrPointeeType(
+ const ast_matchers::internal::Matcher<QualType> &TypeMatcher) {
+ return anyOf(hasType(TypeMatcher),
+ hasType(pointerType(pointee(TypeMatcher))));
+}
+
+constexpr llvm::StringLiteral StdCopyNHeader = "<algorithm>";
+} // namespace
ReplaceMemcpyWithStdCopy::ReplaceMemcpyWithStdCopy(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
- utils::IncludeSorter::IS_LLVM)) {
-}
+ Inserter(Options.getLocalOrGlobal("IncludeStyle",
+ utils::IncludeSorter::IS_LLVM),
+ areDiagsSelfContained()) {}
void ReplaceMemcpyWithStdCopy::registerMatchers(MatchFinder *Finder) {
- assert(Finder != nullptr);
+ const auto ByteLikeTypeWithId = [](const char *ID) {
+ return anything();
+ // return hasCanonicalType(matchers::isSimpleChar());
+ };
+
+ const auto IsByteSequence = anything();
+ // hasTypeOrPointeeType(
+ // hasCanonicalType(hasDeclaration(cxxRecordDecl(hasAnyName({
+ // "::std::vector",
+ // "::std::span",
+ // "::std::deque",
+ // "::std::array",
+ // "::std::string",
+ // "::std::string_view",
+ // })))));
+ // __jm__ for template classes need to add a check whether T is ByteLike
+ // __jm__ these matchers unused because memcpy for non-byte sequences should
+ // be flagged without FixIt
- if (!getLangOpts().CPlusPlus)
- return;
+ const auto IsDestContainerData = cxxMemberCallExpr(
+ callee(cxxMethodDecl(
+ hasName("data"),
+ returns(pointerType(pointee(ByteLikeTypeWithId("data_return_type")))),
+ unless(isConst()))),
+ argumentCountIs(0), on(expr(IsByteSequence)));
- auto MemcpyMatcher =
- callExpr(hasDeclaration(functionDecl(hasName("memcpy"),
- isExpansionInSystemHeader())),
- isExpansionInMainFile())
- .bind("memcpy_function");
+ const auto IsSourceContainerData = cxxMemberCallExpr(
+ callee(cxxMethodDecl(
+ hasName("data"),
+ returns(pointerType(pointee(ByteLikeTypeWithId("data_return_type"))))
+ //,isConst() __jm__ doesn't have to be const pre-implicit cast, but
+ // that would require switching traversal type
+ )),
+ argumentCountIs(0),
+ on(expr(IsByteSequence))); // __jm__ how to express that the caller needs
+ // to be const qualified / a const lvalue?
- Finder->addMatcher(MemcpyMatcher, this);
+ auto IsDestCArrayOrContainerData =
+ anyOf(IsDestContainerData.bind("dest_data"),
+ hasType(hasCanonicalType(arrayType().bind("dest_array"))));
+
+ auto IsSourceCArrayOrContainerData =
+ anyOf(IsSourceContainerData, hasType(hasCanonicalType(arrayType())));
+ // all of the pointer args to memcpy must be any of:
+ // 1. .data() call to record which
+ // can be passed to std::begin or has .begin method and
+ // can be passed to std::end or has .end method and
+ // can be passed to std::size or has .size method
+ // 2. static c-array
+ const auto ReturnValueUsed =
+ optionally(hasParent(compoundStmt().bind("return_value_discarded")));
+
+ const auto MemcpyDecl =
+ functionDecl(hasAnyName("::std::memcpy", "::memcpy")
+ // , argumentCountIs(3) __jm__ doesn't work for
+ // functionDecl, possibly only for callExpr?
+ );
+ // __jm__ also match on arg types
+ const auto Expression =
+ callExpr(callee(MemcpyDecl), ReturnValueUsed,
+ hasArgument(0, expr(IsDestCArrayOrContainerData).bind("dest")),
+ hasArgument(1, IsSourceCArrayOrContainerData));
+ Finder->addMatcher(Expression.bind(MemcpyRef), this);
}
void ReplaceMemcpyWithStdCopy::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
- if (!getLangOpts().CPlusPlus)
- return;
+ Inserter.registerPreprocessor(PP);
+}
- Inserter =
- std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
- IncludeStyle);
- PP->addPPCallbacks(Inserter->CreatePPCallbacks());
+void ReplaceMemcpyWithStdCopy::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle", Inserter.getStyle());
}
-void ReplaceMemcpyWithStdCopy::check(const MatchFinder::MatchResult &Result) {
- const auto *MemcpyNode = Result.Nodes.getNodeAs<CallExpr>("memcpy_function");
- assert(MemcpyNode != nullptr);
+// Determine if the result of an expression is "stored" in some way.
+// It is true if the value is stored into a variable or used as initialization
+// or passed to a function or constructor.
+// For this use case compound assignments are not counted as a "store" (the 'E'
+// expression should have pointer type).
+static bool isExprValueStored(const Expr *E, ASTContext &C) {
+ E = E->IgnoreParenCasts();
+ // Get first non-paren, non-cast parent.
+ ParentMapContext &PMap = C.getParentMapContext();
+ DynTypedNodeList P = PMap.getParents(*E);
+ if (P.size() != 1)
+ return false;
+ const Expr *ParentE = nullptr;
+ while ((ParentE = P[0].get<Expr>()) && ParentE->IgnoreParenCasts() == E) {
+ P = PMap.getParents(P[0]);
+ if (P.size() != 1)
+ return false;
+ }
+
+ if (const auto *ParentVarD = P[0].get<VarDecl>())
+ return ParentVarD->getInit()->IgnoreParenCasts() == E;
+
+ if (!ParentE)
+ return false;
- DiagnosticBuilder Diag =
- diag(MemcpyNode->getExprLoc(), "use std::copy instead of memcpy");
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(ParentE))
+ return BinOp->getOpcode() == BO_Assign &&
+ BinOp->getRHS()->IgnoreParenCasts() == E;
- renameFunction(Diag, MemcpyNode);
- reorderArgs(Diag, MemcpyNode);
- insertHeader(Diag, MemcpyNode, Result.SourceManager);
+ return isa<CallExpr, CXXConstructExpr>(ParentE);
}
+#include "llvm/Support/Debug.h"
+#define DEBUG_TYPE "clang-tidy"
-void ReplaceMemcpyWithStdCopy::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "IncludeStyle",
- utils::IncludeSorter::toString(IncludeStyle));
+bool ReplaceMemcpyWithStdCopy::checkIsByteSequence(
+ const MatchFinder::MatchResult &Result, std::string_view Prefix) {
+
+ const auto &ArgNode = *Result.Nodes.getNodeAs<Expr>("dest");
+ ArgNode.dumpColor();
+
+ auto PointerType_ = ArgNode.getType()->getAs<PointerType>();
+ if (PointerType_ == nullptr)
+ return false;
+
+ auto PointeeType = PointerType_->getPointeeType();
+ // if
+ // (StructFieldTy->isIncompleteType())
+ // return false;
+ auto PointeeWidth =
+ Result.Context->getTypeInfo(PointeeType.getTypePtr()).Width;
+
+ bool IsArgPtrToBytelike = PointeeWidth == Result.Context->getCharWidth();
+
+ LLVM_DEBUG(llvm::dbgs() << "__jm__ dbgs\n");
+ // ArgNode.dumpPretty(*Result.Context);
+ ArgNode.dumpColor();
+ if (ArgNode.IgnoreImplicit()->getType()->isArrayType()) {
+ LLVM_DEBUG(llvm::dbgs()
+ << name<CArray>() << "__jm__ array \n typewidth:" << PointeeWidth
+ << "\n charwidth:" << Result.Context->getCharWidth());
+
+ return IsArgPtrToBytelike;
+ }
+
+ // ArgNode is a result of either std::data or .data()
+ if (const auto *MC =
+ dyn_cast_or_null<CXXMemberCallExpr>(ArgNode.IgnoreCasts());
+ MC != nullptr) {
+ // return false;
+ LLVM_DEBUG(llvm::dbgs() << "__jm__ member call");
+ // diag(ArgNode.getExprLoc(), "%0 isnull")
+ // << (MC->getMethodDecl() == nullptr);
+ } else {
+ LLVM_DEBUG(llvm::dbgs() << "__jm__ otherwise");
+ }
+ return false;
+ // else if (auto *FC = P->get<CallExpr>(); FC != nullptr) {
+ // diag(ArgNode.getExprLoc(), "%0 _2 name is")
+ // <<
+ // FC->getCalleeDecl()->getCanonicalDecl()->getAsFunction()->getName();
+ // }
}
-void ReplaceMemcpyWithStdCopy::renameFunction(DiagnosticBuilder &Diag,
- const CallExpr *MemcpyNode) {
- const CharSourceRange FunctionNameSourceRange = CharSourceRange::getCharRange(
- MemcpyNode->getBeginLoc(), MemcpyNode->getArg(0)->getBeginLoc());
+void ReplaceMemcpyWithStdCopy::check(const MatchFinder::MatchResult &Result) {
+ const auto *MemcpyNode = Result.Nodes.getNodeAs<CallExpr>(MemcpyRef);
+ assert(MemcpyNode != nullptr && "Matched node cannot be null");
+
+ auto Diag = diag(MemcpyNode->getExprLoc(), "prefer std::copy_n to memcpy");
+
+ // don't issue a fixit if the result of the call is used
+ // if (bool IsMemcpyReturnValueUsed =
+ // Result.Nodes.getNodeAs<Expr>("return_value_discarded") == nullptr;
+ // IsMemcpyReturnValueUsed)
+ // return;
+
+ auto *DestArg = MemcpyNode->getArg(0);
+ auto *SrcArg = MemcpyNode->getArg(1);
+ auto *SizeArg = MemcpyNode->getArg(2);
+
+ assert(DestArg != nullptr and SrcArg != nullptr and SizeArg != nullptr and
+ "Call node arguments cannot be null");
+
+ if (not checkIsByteSequence(Result, "dest")
+ // or
+ // not checkIsByteSequence(Result, *SrcArg, Diag)
+ )
+ diag(MemcpyNode->getExprLoc(), "not a byte sequence");
+
+ // // __jm__ strip .data() / std::data calls
+
+ // // const CharSourceRange FunctionNameSourceRange =
+ // // CharSourceRange::getCharRange(
+ // // MemcpyNode->getBeginLoc(), MemcpyNode->getArg(0)->getBeginLoc());
+ // Diag << FixItHint::CreateReplacement(
+ // MemcpyNode->getCallee()->getSourceRange(), "std::copy_n");
- Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, "std::copy(");
+ {
+ // using namespace std::literals;
+ // Diag << FixItHint::CreateReplacement(
+ // DestArg->getSourceRange(),
+ // ("std::begin(" +
+ // tooling::fixit::getText(SrcArg->getSourceRange(), *Result.Context) +
+ // ")")
+ // .str());
+ // Diag << FixItHint::CreateReplacement(
+ // SrcArg->getSourceRange(),
+ // tooling::fixit::getText(SizeArg->getSourceRange(), *Result.Context));
+ // Diag << FixItHint::CreateReplacement(
+ // SizeArg->getSourceRange(),
+ // ("std::begin(" +
+ // tooling::fixit::getText(DestArg->getSourceRange(), *Result.Context)
+ // +
+ // ")")
+ // .str());
+ }
+ // dest source size -> source size dest
+ // renameFunction(Diag, MemcpyNode);
+ // reorderArgs(Diag, MemcpyNode);
+ // insertHeader(Diag, MemcpyNode, Result.SourceManager);
+
+ Diag << Inserter.createIncludeInsertion(
+ Result.SourceManager->getFileID(MemcpyNode->getBeginLoc()),
+ StdCopyNHeader);
}
+// void ReplaceMemcpyWithStdCopy::handleMemcpy(const CallExpr &Node) {
+
+// // FixIt
+// const CharSourceRange FunctionNameSourceRange =
+// CharSourceRange::getCharRange(
+// Node.getBeginLoc(), Node.getArg(0)->getBeginLoc());
+
+// Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
+// "std::copy_n(");
+// }
+
+void ReplaceMemcpyWithStdCopy::renameFunction(DiagnosticBuilder &Diag,
+ const CallExpr *MemcpyNode) {}
+
void ReplaceMemcpyWithStdCopy::reorderArgs(DiagnosticBuilder &Diag,
const CallExpr *MemcpyNode) {
- std::array<std::string, 3> arg;
+ 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);
+ 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 {
+ 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]);
+ Diag << FixItHint::CreateReplacement(GetSourceRange(0), Arg[1]);
- arg[2] = arg[1] + " + ((" + arg[2] + ") / sizeof(*(" + arg[1] + ")))";
- Diag << FixItHint::CreateReplacement(getSourceRange(1), arg[2]);
+ Arg[2] = Arg[1] + " + ((" + Arg[2] + ") / sizeof(*(" + Arg[1] + ")))";
+ Diag << FixItHint::CreateReplacement(GetSourceRange(1), Arg[2]);
- Diag << FixItHint::CreateReplacement(getSourceRange(2), arg[0]);
+ 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
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h
index 0f262bf839af24..0f847a39f474d5 100644
--- a/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h
+++ b/clang-tools-extra/clang-tidy/modernize/ReplaceMemcpyWithStdCopy.h
@@ -11,13 +11,10 @@
#include "../ClangTidyCheck.h"
#include "../utils/IncludeInserter.h"
-#include <memory>
-#include <string>
-#include <vector>
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/Basic/LangOptions.h"
-namespace clang {
-namespace tidy {
-namespace modernize {
+namespace clang::tidy::modernize {
// Replace the C memcpy function with std::copy
class ReplaceMemcpyWithStdCopy : public ClangTidyCheck {
@@ -27,23 +24,31 @@ class ReplaceMemcpyWithStdCopy : public ClangTidyCheck {
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;
+ 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:
- std::unique_ptr<utils::IncludeInserter> Inserter;
- utils::IncludeInserter IncludeInserter;
- const utils::IncludeSorter::IncludeStyle IncludeStyle;
+ utils::IncludeInserter Inserter;
};
-} // namespace modernize
-} // namespace tidy
-} // namespace clang
+} // namespace clang::tidy::modernize
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_MEMCPY_WITH_STDCOPY_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 2d14f5ec913239..cd256bb8074f88 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -217,8 +217,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-memcpy-with-std-copy
+ <clang-tidy/checks/modernize-replace-memcpy-with-std-copy>` 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 de8197f20b39a4..e923f43dd44a67 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -286,7 +286,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-memcpy-with-std-copy <modernize/replace-memcpy-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/replace-memcpy-with-stdcopy.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-memcpy-with-stdcopy.rst
new file mode 100644
index 00000000000000..8708fe3b6e665e
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/replace-memcpy-with-stdcopy.rst
@@ -0,0 +1,47 @@
+.. title:: clang-tidy - modernize-replace-memcpy-with-std-copy
+
+modernize-replace-memcpy-with-std-copy
+===================================
+
+Replaces all occurrences of the C ``memcpy`` function with ``std::copy``
+
+Example:
+
+.. code-block:: c++
+
+ /*!
+ * \param destination Pointer to the destination array where the content is to be copied
+ * \param source Pointer to the source of data to be copied
+ * \param num Number of bytes to copy
+ */
+ memcpy(destination, source, num);
+
+becomes
+
+.. code-block:: c++
+
+ /*!
+ * \param destination Pointer to the destination array where the content is to be copied
+ * \param source Pointer to the source of data to be copied
+ * \param num Number of bytes to copy
+ */
+ std::copy(source, source + (num / sizeof *source), destination);
+
+Bytes to iterator conversion
+----------------------------
+
+Unlike ``std::copy`` that take an iterator on the last element of the source array, ``memcpy`` request the number of bytes to copy.
+In order to make the check working, it will convert the size parameter to an iterator by replacing it by ``source + (num / sizeof *source)``
+
+Header inclusion
+----------------
+
+``std::copy`` being provided by the ``algorithm`` header file, this check will include it if needed.
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, `llvm` or `google`. Default
+ is `llvm`.
More information about the cfe-commits
mailing list