[clang-tools-extra] [clang-tidy] Adds a SuspiciousCopyInRangeLoop check. (PR #152970)
Julia Hansbrough via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 11 01:32:13 PDT 2025
https://github.com/flowerhack created https://github.com/llvm/llvm-project/pull/152970
See documentation for purpose and further details.
>From d1e7774b62a71da39f050ddb256322245481eb42 Mon Sep 17 00:00:00 2001
From: Julia Hansbrough <flowerhack at google.com>
Date: Sat, 2 Aug 2025 01:33:09 +0000
Subject: [PATCH] [clang-tidy] Adds a SuspiciousCopyInRangeLoop check.
See documentation for purpose and further details.
---
.../bugprone/BugproneTidyModule.cpp | 3 ++
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../SuspiciousCopyInRangeLoopCheck.cpp | 44 +++++++++++++++++++
.../bugprone/SuspiciousCopyInRangeLoopCheck.h | 34 ++++++++++++++
.../suspicious-copy-in-range-loop.rst | 31 +++++++++++++
.../suspicious-copy-in-range-loop.cpp | 7 +++
6 files changed, 120 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/SuspiciousCopyInRangeLoopCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/SuspiciousCopyInRangeLoopCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-copy-in-range-loop.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-copy-in-range-loop.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 824ebdfbd00dc..1e08d9b0ec977 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -73,6 +73,7 @@
#include "StringIntegerAssignmentCheck.h"
#include "StringLiteralWithEmbeddedNulCheck.h"
#include "StringviewNullptrCheck.h"
+#include "SuspiciousCopyInRangeLoopCheck.h"
#include "SuspiciousEnumUsageCheck.h"
#include "SuspiciousIncludeCheck.h"
#include "SuspiciousMemoryComparisonCheck.h"
@@ -153,6 +154,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-incorrect-enable-if");
CheckFactories.registerCheck<IncorrectEnableSharedFromThisCheck>(
"bugprone-incorrect-enable-shared-from-this");
+ CheckFactories.registerCheck<SuspiciousCopyInRangeLoopCheck>(
+ "bugprone-suspicious-copy-in-range-loop");
CheckFactories.registerCheck<UnintendedCharOstreamOutputCheck>(
"bugprone-unintended-char-ostream-output");
CheckFactories.registerCheck<ReturnConstRefFromParameterCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 59928e5e47a09..c1835b5e4f82b 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -33,6 +33,7 @@ add_clang_library(clangTidyBugproneModule STATIC
InvalidEnumDefaultInitializationCheck.cpp
UnintendedCharOstreamOutputCheck.cpp
ReturnConstRefFromParameterCheck.cpp
+ SuspiciousCopyInRangeLoopCheck.cpp
SuspiciousStringviewDataUsageCheck.cpp
SwitchMissingDefaultCaseCheck.cpp
IncDecInConditionsCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousCopyInRangeLoopCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SuspiciousCopyInRangeLoopCheck.cpp
new file mode 100644
index 0000000000000..9f93a6bc2aeb0
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousCopyInRangeLoopCheck.cpp
@@ -0,0 +1,44 @@
+//===--- suspiciousCopyInRangeLoopCheck.cpp - clang-tidy
+//-------------------------===//
+//
+// 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 "SuspiciousCopyInRangeLoopCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+// TODO: Possibly rename to "SuspiciousAutoCopyInRangeLoop"
+void SuspiciousCopyInRangeLoopCheck::registerMatchers(MatchFinder *Finder) {
+ // TODO: make sure this catches `const auto` as well.
+ auto auto_copy_in_range_based_for_loops =
+ cxxForRangeStmt(hasLoopVariable(varDecl(hasType(autoType()))));
+ Finder->addMatcher(auto_copy_in_range_based_for_loops.bind("for_range"),
+ this);
+}
+
+void SuspiciousCopyInRangeLoopCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl =
+ Result.Nodes.getNodeAs<CXXForRangeStmt>("for_range");
+ std::string VarName = MatchedDecl->getLoopVariable()->getNameAsString();
+ if (MatchedDecl) {
+ diag(MatchedDecl->getBeginLoc(),
+ "Found potentially-spurious copy in range loop:\n* It is unlikely you "
+ "intended to copy '%0' for each iteration of the loop\n* To avoid "
+ "copying, use `auto&`\n* If this copy was intentional, do not use "
+ "`auto` and instead spell out the type explicitly")
+ << VarName
+ << FixItHint::CreateInsertion(
+ MatchedDecl->getLoopVariable()->getLocation(),
+ "suspicious copy here");
+ }
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousCopyInRangeLoopCheck.h b/clang-tools-extra/clang-tidy/bugprone/SuspiciousCopyInRangeLoopCheck.h
new file mode 100644
index 0000000000000..fe8c7bad76ae0
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousCopyInRangeLoopCheck.h
@@ -0,0 +1,34 @@
+//===--- SuspiciousCopyInRangeLoopCheck.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_BUGPRONE_SUSPICIOUSCOPYINRANGELOOPCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSCOPYINRANGELOOPCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// FIXME: Write a short description.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/supsicious-copy-in-range-loop.html
+class SuspiciousCopyInRangeLoopCheck : public ClangTidyCheck {
+public:
+ SuspiciousCopyInRangeLoopCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSCOPYINRANGELOOPCHECK_H
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-copy-in-range-loop.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-copy-in-range-loop.rst
new file mode 100644
index 0000000000000..6add3192fb7fa
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-copy-in-range-loop.rst
@@ -0,0 +1,31 @@
+.. title:: clang-tidy - bugprone-suspicious-copy-in-range-loop
+
+bugprone-suspicious-copy-in-range-loop
+================================
+
+This check finds ``for`` loops that are potentially-unintentionally copying
+the loop variable unnecessarily.
+
+For instance, this check would warn on a loop of the form:
+
+```for (auto s : strings) {
+ ...
+}
+```
+
+This can lead to bugs when the programmer operates on `s` in the loop, assuming
+they are mutating the elements directly, but they are in fact only mutating a
+temporary copy.
+
+In cases where the programmer's intention is to in fact copy the variable,
+the warning can be suppressed by explicitly stating the type.
+For instance, this check will *not* warn on a loop of the form
+
+```for (std::string s : strings) {
+ ...
+ }
+ ```
+
+This check is different from `performance-for-range-copy` in that
+it triggers on *every* instance where this pattern occurs rather than
+potentially-expensive ones.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-copy-in-range-loop.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-copy-in-range-loop.cpp
new file mode 100644
index 0000000000000..3316c28e06f88
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-copy-in-range-loop.cpp
@@ -0,0 +1,7 @@
+// RUN: %check_clang_tidy %s bugprone-suspicious-copy-in-range-loop %t
+
+std::vector<std::string> some_strings;
+for (auto x : some_strings) {
+ std::cout << x << "\n";
+}
+// CHECK-MESSAGES: foo
More information about the cfe-commits
mailing list