https://github.com/ayyYoSam created
https://github.com/llvm/llvm-project/pull/226742
# Description
Fixes: #226720
The check detects range-based loops that insert each element individually into
associative containers and suggests replacing them with a bulk `insert()` call.
For example:
```cpp
for (int i : in) {
out.insert(i);
}
```
is transformed into:
```cpp
out.insert(in.begin(), in.end());
```
The initial implementation covers standard associative containers including
`set`, `map`, and `unordered_*` variants.
## Changes
* Added `UseBulkInsertCheck.cpp`
* Added `UseBulkInsertCheck.h`
* Added the corresponding test file
* Registered `UseBulkInsertCheck` in `MiscTidyModule.cpp`
* Added the check to the `misc` module's `CMakeLists.txt`
## Test
I have not performed local build or test validation. I expect the CI checks to
provide validation, and I am happy to address any issues identified during
review.
>From 4ef080191328fa39e845391172c40ac6869e05f9 Mon Sep 17 00:00:00 2001
From: ayyYoSam <[email protected]>
Date: Sat, 26 Sep 2026 22:47:25 -0300
Subject: [PATCH] clang-tidy: add misc-use-bulk-insert check
---
.../clang-tidy/misc/CMakeLists.txt | 1 +
.../clang-tidy/misc/MiscTidyModule.cpp | 3 +
.../clang-tidy/misc/UseBulkInsertCheck.cpp | 97 +++++++++++++++++++
.../clang-tidy/misc/UseBulkInsertCheck.h | 26 +++++
.../checkers/misc/use-bulk-insert.cpp | 22 +++++
5 files changed, 149 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/misc/UseBulkInsertCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/misc/UseBulkInsertCheck.h
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/misc/use-bulk-insert.cpp
diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
index 68faf77fa1d5f..faa27bb5fa77e 100644
--- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
@@ -20,6 +20,7 @@ set_target_properties(genconfusable PROPERTIES FOLDER "Clang
Tools Extra/Sourceg
add_clang_library(clangTidyMiscModule STATIC
AnonymousNamespaceInHeaderCheck.cpp
ConfusableIdentifierCheck.cpp
+ UseBulkInsertCheck.cpp
ConstCorrectnessCheck.cpp
CoroutineHostileRAIICheck.cpp
DefinitionsInHeadersCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
index 52d3b4297ba26..07f1517eec661 100644
--- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "../ClangTidy.h"
+#include "UseBulkInsertCheck.h"
#include "../ClangTidyModule.h"
#include "AnonymousNamespaceInHeaderCheck.h"
#include "ConfusableIdentifierCheck.h"
@@ -49,6 +50,8 @@ class MiscModule : public ClangTidyModule {
"misc-anonymous-namespace-in-header");
CheckFactories.registerCheck<ConfusableIdentifierCheck>(
"misc-confusable-identifiers");
+ CheckFactories.registerCheck<UseBulkInsertCheck>(
+ "misc-use-bulk-insert");
CheckFactories.registerCheck<ConstCorrectnessCheck>(
"misc-const-correctness");
CheckFactories.registerCheck<CoroutineHostileRAIICheck>(
diff --git a/clang-tools-extra/clang-tidy/misc/UseBulkInsertCheck.cpp
b/clang-tools-extra/clang-tidy/misc/UseBulkInsertCheck.cpp
new file mode 100644
index 0000000000000..dbda841ee5e61
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/misc/UseBulkInsertCheck.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "UseBulkInsertCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::misc {
+
+void UseBulkInsertCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ cxxForRangeStmt(
+ hasLoopVariable(varDecl().bind("loop_var")),
+ hasRangeInit(expr().bind("range")),
+ hasBody(compoundStmt(
+ statementCountIs(1),
+ hasAnySubstatement(cxxMemberCallExpr(
+ callee(memberExpr(
+ ofClass(cxxRecordDecl(hasAnyName(
+ "::std::set", "::std::map", "::std::multiset",
+ "::std::multimap", "::std::unordered_set",
+ "::std::unordered_map",
+ "::std::unordered_multiset",
+ "::std::unordered_multimap"))))),
+ argumentCountIs(1),
+ hasArgument(
+ 0, ignoringParenImpCasts(
+ declRefExpr(to(varDecl().bind("insert_arg"))))))
+ .bind("insert_call")))))
+ .bind("for_range"),
+ this);
+}
+
+void UseBulkInsertCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Loop =
+ Result.Nodes.getNodeAs<CXXForRangeStmt>("for_range");
+ const auto *LoopVar =
+ Result.Nodes.getNodeAs<VarDecl>("loop_var");
+ const auto *InsertArg =
+ Result.Nodes.getNodeAs<VarDecl>("insert_arg");
+ const auto *Range =
+ Result.Nodes.getNodeAs<Expr>("range");
+ const auto *InsertCall =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>("insert_call");
+
+ if (!Loop || !LoopVar || !InsertArg || !Range || !InsertCall)
+ return;
+
+ if (LoopVar != InsertArg)
+ return;
+
+ const auto *Member = dyn_cast<MemberExpr>(InsertCall->getCallee());
+ if (!Member)
+ return;
+
+ const Expr *Object = Member->getBase();
+ if (!Object)
+ return;
+
+ const SourceManager &SM = *Result.SourceManager;
+ const LangOptions &LangOpts = Result.Context->getLangOpts();
+
+ StringRef ObjectText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Object->getSourceRange()), SM,
+ LangOpts);
+
+ StringRef RangeText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Range->getSourceRange()), SM,
+ LangOpts);
+
+ if (ObjectText.empty() || RangeText.empty())
+ return;
+
+ std::string Replacement = ObjectText.str();
+ Replacement += ".insert(";
+ Replacement += RangeText;
+ Replacement += ".begin(), ";
+ Replacement += RangeText;
+ Replacement += ".end());";
+
+ diag(Loop->getForLoc(),
+ "use bulk insertion instead of inserting elements one at a time")
+ << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(Loop->getBeginLoc(),
+ Loop->getEndLoc()),
+ Replacement);
+}
+
+} // namespace clang::tidy::misc
\ No newline at end of file
diff --git a/clang-tools-extra/clang-tidy/misc/UseBulkInsertCheck.h
b/clang-tools-extra/clang-tidy/misc/UseBulkInsertCheck.h
new file mode 100644
index 0000000000000..502cf1d29a55e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/misc/UseBulkInsertCheck.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_MISC_USEBULKINSERTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEBULKINSERTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::misc {
+
+class UseBulkInsertCheck : public ClangTidyCheck {
+public:
+ using ClangTidyCheck::ClangTidyCheck;
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::misc
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEBULKINSERTCHECK_H
\ No newline at end of file
diff --git
a/clang-tools-extra/test/clang-tidy/checkers/misc/use-bulk-insert.cpp
b/clang-tools-extra/test/clang-tidy/checkers/misc/use-bulk-insert.cpp
new file mode 100644
index 0000000000000..9b524ba3bcaaf
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-bulk-insert.cpp
@@ -0,0 +1,22 @@
+// RUN: %check_clang_tidy %s misc-use-bulk-insert %t
+
+#include <set>
+#include <vector>
+
+void test_set(const std::set<int> &In) {
+ std::set<int> Out;
+
+ for (int I : In) {
+ Out.insert(I);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use bulk insertion instead of
inserting elements one at a time [misc-use-bulk-insert]
+ // CHECK-FIXES: Out.insert(In.begin(), In.end());
+ }
+}
+
+void test_vector(const std::vector<int> &In) {
+ std::vector<int> Out;
+
+ for (int I : In) {
+ Out.insert(I);
+ }
+}
\ No newline at end of file
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits