Skip to content

[clang-tidy] Add new check bugprone-capture-this-by-field #130297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

HerrCai0907
Copy link
Contributor

@HerrCai0907 HerrCai0907 commented Mar 7, 2025

Finds lambda captures that capture the this pointer and store it as class
members without handle the copy and move constructors and the assignments.

Capture this in a lambda and store it as a class member is dangerous because the
lambda can outlive the object it captures. Especially when the object is copied
or moved, the captured this pointer will be implicitly propagated to the
new object. Most of the time, people will believe that the captured this
pointer points to the new object, which will lead to bugs.

Fixes: #120863

@llvmbot
Copy link
Member

llvmbot commented Mar 7, 2025

@llvm/pr-subscribers-clang-tools-extra

@llvm/pr-subscribers-clang-tidy

Author: Congcong Cai (HerrCai0907)

Changes

Finds lambda captures that capture the this pointer and store it as class
members without handle the copy and move constructors and the assignments.

Capture this in a lambda and store it as a class member is dangerous because the
lambda can outlive the object it captures. Especially when the object is copied
or moved, the captured this pointer will be implicitly propagated to the
new object. Most of the time, people will believe that the captured this
pointer points to the new object, which will lead to bugs.


Full diff: https://github.com/llvm/llvm-project/pull/130297.diff

8 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp (+3)
  • (modified) clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt (+1)
  • (added) clang-tools-extra/clang-tidy/bugprone/CaptureThisByFieldCheck.cpp (+99)
  • (added) clang-tools-extra/clang-tidy/bugprone/CaptureThisByFieldCheck.h (+39)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+6)
  • (added) clang-tools-extra/docs/clang-tidy/checks/bugprone/capture-this-by-field.rst (+28)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
  • (added) clang-tools-extra/test/clang-tidy/checkers/bugprone/capture-this-by-field.cpp (+133)
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 0a3376949b6e5..ee9fa5ef06c7a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -16,6 +16,7 @@
 #include "BitwisePointerCastCheck.h"
 #include "BoolPointerImplicitConversionCheck.h"
 #include "BranchCloneCheck.h"
+#include "CaptureThisByFieldCheck.h"
 #include "CastingThroughVoidCheck.h"
 #include "ChainedComparisonCheck.h"
 #include "ComparePointerToMemberVirtualFunctionCheck.h"
@@ -118,6 +119,8 @@ class BugproneModule : public ClangTidyModule {
     CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
         "bugprone-bool-pointer-implicit-conversion");
     CheckFactories.registerCheck<BranchCloneCheck>("bugprone-branch-clone");
+    CheckFactories.registerCheck<CaptureThisByFieldCheck>(
+        "bugprone-capture-this-by-field");
     CheckFactories.registerCheck<CastingThroughVoidCheck>(
         "bugprone-casting-through-void");
     CheckFactories.registerCheck<ChainedComparisonCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index dab139b77c770..4d1d50c4ea2a0 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -12,6 +12,7 @@ add_clang_library(clangTidyBugproneModule STATIC
   BoolPointerImplicitConversionCheck.cpp
   BranchCloneCheck.cpp
   BugproneTidyModule.cpp
+  CaptureThisByFieldCheck.cpp
   CastingThroughVoidCheck.cpp
   ChainedComparisonCheck.cpp
   ComparePointerToMemberVirtualFunctionCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/CaptureThisByFieldCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CaptureThisByFieldCheck.cpp
new file mode 100644
index 0000000000000..1f0f68acf335f
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/CaptureThisByFieldCheck.cpp
@@ -0,0 +1,99 @@
+//===--- CaptureThisByFieldCheck.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 "CaptureThisByFieldCheck.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersMacros.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+namespace {
+
+AST_MATCHER(CXXRecordDecl, correctHandleCaptureThisLambda) {
+  // unresolved
+  if (Node.needsOverloadResolutionForCopyConstructor() &&
+      Node.needsImplicitCopyConstructor())
+    return false;
+  if (Node.needsOverloadResolutionForMoveConstructor() &&
+      Node.needsImplicitMoveConstructor())
+    return false;
+  if (Node.needsOverloadResolutionForCopyAssignment() &&
+      Node.needsImplicitCopyAssignment())
+    return false;
+  if (Node.needsOverloadResolutionForMoveAssignment() &&
+      Node.needsImplicitMoveAssignment())
+    return false;
+  // default but not deleted
+  if (Node.hasSimpleCopyConstructor())
+    return false;
+  if (Node.hasSimpleMoveConstructor())
+    return false;
+  if (Node.hasSimpleCopyAssignment())
+    return false;
+  if (Node.hasSimpleMoveAssignment())
+    return false;
+
+  for (CXXConstructorDecl const *C : Node.ctors()) {
+    if (C->isCopyOrMoveConstructor() && C->isDefaulted() && !C->isDeleted())
+      return false;
+  }
+  for (CXXMethodDecl const *M : Node.methods()) {
+    if (M->isCopyAssignmentOperator() && M->isDefaulted() && !M->isDeleted())
+      return false;
+    if (M->isMoveAssignmentOperator() && M->isDefaulted() && !M->isDeleted())
+      return false;
+  }
+  // FIXME: find ways to identifier correct handle capture this lambda
+  return true;
+}
+
+} // namespace
+
+void CaptureThisByFieldCheck::registerMatchers(MatchFinder *Finder) {
+  auto IsStdFunctionField =
+      fieldDecl(hasType(cxxRecordDecl(hasName("::std::function"))))
+          .bind("field");
+  auto CaptureThis = lambdaCapture(anyOf(
+      // [this]
+      capturesThis(),
+      // [self = this]
+      capturesVar(varDecl(hasInitializer(cxxThisExpr())))));
+  auto IsInitWithLambda = cxxConstructExpr(hasArgument(
+      0,
+      lambdaExpr(hasAnyCapture(CaptureThis.bind("capture"))).bind("lambda")));
+  Finder->addMatcher(
+      cxxRecordDecl(
+          has(cxxConstructorDecl(
+              unless(isCopyConstructor()), unless(isMoveConstructor()),
+              hasAnyConstructorInitializer(cxxCtorInitializer(
+                  isMemberInitializer(), forField(IsStdFunctionField),
+                  withInitializer(IsInitWithLambda))))),
+          unless(correctHandleCaptureThisLambda())),
+      this);
+}
+
+void CaptureThisByFieldCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Capture = Result.Nodes.getNodeAs<LambdaCapture>("capture");
+  const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
+  const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field");
+  diag(Lambda->getBeginLoc(),
+       "using lambda expressions to capture this and storing it in class "
+       "member will cause potential variable lifetime issue when the class "
+       "instance is moved or copied")
+      << Capture->getLocation();
+  diag(Field->getLocation(),
+       "'std::function' that stores captured this and becomes invalid during "
+       "copying or moving",
+       DiagnosticIDs::Note);
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/CaptureThisByFieldCheck.h b/clang-tools-extra/clang-tidy/bugprone/CaptureThisByFieldCheck.h
new file mode 100644
index 0000000000000..72c0a540a7743
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/CaptureThisByFieldCheck.h
@@ -0,0 +1,39 @@
+//===--- CaptureThisByFieldCheck.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_CAPTURETHISBYFIELDCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CAPTURETHISBYFIELDCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include <optional>
+
+namespace clang::tidy::bugprone {
+
+/// Finds lambda captures that capture the ``this`` pointer and store it as class
+/// members without handle the copy and move constructors and the assignments.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/capture-this-by-field.html
+class CaptureThisByFieldCheck : public ClangTidyCheck {
+public:
+  CaptureThisByFieldCheck(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.CPlusPlus11;
+  }
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TraversalKind::TK_IgnoreUnlessSpelledInSource;
+  }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CAPTURETHISBYFIELDCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index ce1418a2a7d58..40c5ee9f60db0 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -91,6 +91,12 @@ Improvements to clang-tidy
 New checks
 ^^^^^^^^^^
 
+- New :doc:`bugprone-capture-this-by-field
+  <clang-tidy/checks/bugprone/capture-this-by-field>` check.
+
+  Finds lambda captures that capture the ``this`` pointer and store it as class
+  members without handle the copy and move constructors and the assignments.
+
 - New :doc:`bugprone-unintended-char-ostream-output
   <clang-tidy/checks/bugprone/unintended-char-ostream-output>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/capture-this-by-field.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/capture-this-by-field.rst
new file mode 100644
index 0000000000000..c837ce5cb6d68
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/capture-this-by-field.rst
@@ -0,0 +1,28 @@
+.. title:: clang-tidy - bugprone-capture-this-by-field
+
+bugprone-capture-this-by-field
+==============================
+
+Finds lambda captures that capture the ``this`` pointer and store it as class
+members without handle the copy and move constructors and the assignments.
+
+Capture this in a lambda and store it as a class member is dangerous because the
+lambda can outlive the object it captures. Especially when the object is copied
+or moved, the captured ``this`` pointer will be implicitly propagated to the
+new object. Most of the time, people will believe that the captured ``this``
+pointer points to the new object, which will lead to bugs.
+
+
+.. code-block:: c++
+
+  struct C {
+    C() : Captured([this]() -> C const * { return this; }) {}
+    std::function<C const *()> Captured;
+  };
+
+  void foo() {
+    C v1{};
+    C v2 = v1; // v2.Captured capture v1's this pointer
+    assert(v2.Captured() == v1.Captured()); // v2.Captured capture v1's this pointer
+    assert(v2.Captured() == &v2); // assertion failed.
+  }
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 5f03ef72cc603..6bef286f28100 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -84,6 +84,7 @@ Clang-Tidy Checks
    :doc:`bugprone-bitwise-pointer-cast <bugprone/bitwise-pointer-cast>`,
    :doc:`bugprone-bool-pointer-implicit-conversion <bugprone/bool-pointer-implicit-conversion>`, "Yes"
    :doc:`bugprone-branch-clone <bugprone/branch-clone>`,
+   :doc:`bugprone-capture-this-by-field <bugprone/capture-this-by-field>`,
    :doc:`bugprone-casting-through-void <bugprone/casting-through-void>`,
    :doc:`bugprone-chained-comparison <bugprone/chained-comparison>`,
    :doc:`bugprone-compare-pointer-to-member-virtual-function <bugprone/compare-pointer-to-member-virtual-function>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/capture-this-by-field.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/capture-this-by-field.cpp
new file mode 100644
index 0000000000000..4ffd6c7ade51e
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/capture-this-by-field.cpp
@@ -0,0 +1,133 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s bugprone-capture-this-by-field %t
+
+namespace std {
+
+template<class Fn>
+class function;
+
+template<class R, class ...Args>
+class function<R(Args...)> {
+public:
+  function() noexcept;
+  template<class F> function(F &&);
+};
+
+} // namespace std
+
+struct Basic {
+  Basic() : Captured([this]() { static_cast<void>(this); }) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: using lambda expressions to capture this and storing it in class member
+  std::function<void()> Captured;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: note: 'std::function' that stores captured this
+};
+
+struct AssignCapture {
+  AssignCapture() : Captured([Self = this]() { static_cast<void>(Self); }) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: using lambda expressions to capture this and storing it in class member
+  std::function<void()> Captured;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: note: 'std::function' that stores captured this
+};
+
+struct DeleteMoveAndCopy {
+  DeleteMoveAndCopy() : Captured([this]() { static_cast<void>(this); }) {}
+  DeleteMoveAndCopy(DeleteMoveAndCopy const&) = delete;
+  DeleteMoveAndCopy(DeleteMoveAndCopy &&) = delete;
+  DeleteMoveAndCopy& operator=(DeleteMoveAndCopy const&) = delete;
+  DeleteMoveAndCopy& operator=(DeleteMoveAndCopy &&) = delete;
+  std::function<void()> Captured;
+};
+
+struct DeleteCopyImplicitDisabledMove {
+  DeleteCopyImplicitDisabledMove() : Captured([this]() { static_cast<void>(this); }) {}
+  DeleteCopyImplicitDisabledMove(DeleteCopyImplicitDisabledMove const&) = delete;
+  DeleteCopyImplicitDisabledMove& operator=(DeleteCopyImplicitDisabledMove const&) = delete;
+  std::function<void()> Captured;
+};
+
+struct DeleteCopyDefaultMove {
+  DeleteCopyDefaultMove() : Captured([this]() { static_cast<void>(this); }) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: using lambda expressions to capture this and storing it in class member
+  DeleteCopyDefaultMove(DeleteCopyDefaultMove const&) = delete;
+  DeleteCopyDefaultMove(DeleteCopyDefaultMove &&) = default;
+  DeleteCopyDefaultMove& operator=(DeleteCopyDefaultMove const&) = delete;
+  DeleteCopyDefaultMove& operator=(DeleteCopyDefaultMove &&) = default;
+  std::function<void()> Captured;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: note: 'std::function' that stores captured this
+};
+
+struct DeleteMoveDefaultCopy {
+  DeleteMoveDefaultCopy() : Captured([this]() { static_cast<void>(this); }) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: using lambda expressions to capture this and storing it in class member
+  DeleteMoveDefaultCopy(DeleteMoveDefaultCopy const&) = default;
+  DeleteMoveDefaultCopy(DeleteMoveDefaultCopy &&) = delete;
+  DeleteMoveDefaultCopy& operator=(DeleteMoveDefaultCopy const&) = default;
+  DeleteMoveDefaultCopy& operator=(DeleteMoveDefaultCopy &&) = delete;
+  std::function<void()> Captured;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: note: 'std::function' that stores captured this
+};
+
+struct DeleteCopyMoveBase {
+  DeleteCopyMoveBase() = default;
+  DeleteCopyMoveBase(DeleteCopyMoveBase const&) = delete;
+  DeleteCopyMoveBase(DeleteCopyMoveBase &&) = delete;
+  DeleteCopyMoveBase& operator=(DeleteCopyMoveBase const&) = delete;
+  DeleteCopyMoveBase& operator=(DeleteCopyMoveBase &&) = delete;
+};
+
+struct Inherit : DeleteCopyMoveBase {
+  Inherit() : DeleteCopyMoveBase{}, Captured([this]() { static_cast<void>(this); }) {}
+  std::function<void()> Captured;
+};
+
+struct UserDefinedCopyMove {
+  UserDefinedCopyMove() : Captured([this]() { static_cast<void>(this); }) {}
+  UserDefinedCopyMove(UserDefinedCopyMove const&);
+  UserDefinedCopyMove(UserDefinedCopyMove &&);
+  UserDefinedCopyMove& operator=(UserDefinedCopyMove const&);
+  UserDefinedCopyMove& operator=(UserDefinedCopyMove &&);
+  std::function<void()> Captured;
+};
+
+struct UserDefinedCopyMoveWithDefault1 {
+  UserDefinedCopyMoveWithDefault1() : Captured([this]() { static_cast<void>(this); }) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: using lambda expressions to capture this and storing it in class member
+  UserDefinedCopyMoveWithDefault1(UserDefinedCopyMoveWithDefault1 const&) = default;
+  UserDefinedCopyMoveWithDefault1(UserDefinedCopyMoveWithDefault1 &&);
+  UserDefinedCopyMoveWithDefault1& operator=(UserDefinedCopyMoveWithDefault1 const&);
+  UserDefinedCopyMoveWithDefault1& operator=(UserDefinedCopyMoveWithDefault1 &&);
+  std::function<void()> Captured;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: note: 'std::function' that stores captured this
+};
+
+struct UserDefinedCopyMoveWithDefault2 {
+  UserDefinedCopyMoveWithDefault2() : Captured([this]() { static_cast<void>(this); }) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: using lambda expressions to capture this and storing it in class member
+  UserDefinedCopyMoveWithDefault2(UserDefinedCopyMoveWithDefault2 const&);
+  UserDefinedCopyMoveWithDefault2(UserDefinedCopyMoveWithDefault2 &&) = default;
+  UserDefinedCopyMoveWithDefault2& operator=(UserDefinedCopyMoveWithDefault2 const&);
+  UserDefinedCopyMoveWithDefault2& operator=(UserDefinedCopyMoveWithDefault2 &&);
+  std::function<void()> Captured;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: note: 'std::function' that stores captured this
+};
+
+struct UserDefinedCopyMoveWithDefault3 {
+  UserDefinedCopyMoveWithDefault3() : Captured([this]() { static_cast<void>(this); }) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: using lambda expressions to capture this and storing it in class member
+  UserDefinedCopyMoveWithDefault3(UserDefinedCopyMoveWithDefault3 const&);
+  UserDefinedCopyMoveWithDefault3(UserDefinedCopyMoveWithDefault3 &&);
+  UserDefinedCopyMoveWithDefault3& operator=(UserDefinedCopyMoveWithDefault3 const&) = default;
+  UserDefinedCopyMoveWithDefault3& operator=(UserDefinedCopyMoveWithDefault3 &&);
+  std::function<void()> Captured;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: note: 'std::function' that stores captured this
+};
+
+struct UserDefinedCopyMoveWithDefault4 {
+  UserDefinedCopyMoveWithDefault4() : Captured([this]() { static_cast<void>(this); }) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: using lambda expressions to capture this and storing it in class member
+  UserDefinedCopyMoveWithDefault4(UserDefinedCopyMoveWithDefault4 const&);
+  UserDefinedCopyMoveWithDefault4(UserDefinedCopyMoveWithDefault4 &&);
+  UserDefinedCopyMoveWithDefault4& operator=(UserDefinedCopyMoveWithDefault4 const&);
+  UserDefinedCopyMoveWithDefault4& operator=(UserDefinedCopyMoveWithDefault4 &&) = default;
+  std::function<void()> Captured;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: note: 'std::function' that stores captured this
+};

Copy link

github-actions bot commented Mar 7, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@HerrCai0907 HerrCai0907 force-pushed the 120863-clang-tidy-check-request-dont-capture-this-by-a-field-of-default-copyable-class branch from a503ee1 to c5702f9 Compare March 8, 2025 02:19
Copy link
Member

@PiotrZSL PiotrZSL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idea is nice.
From functional point of view is ok but check may still require some work to clarify some stuff.


void foo() {
C v1{};
C v2 = v1; // v2.Captured capture v1's this pointer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then shouldn't this check be somehow limited only to classes that got move/copy construction enabled ?
And in such case name maybe could be different, like something with dangling this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand it well. Do you mean we need more document about how to limit it? And mention dangling this in docs also?

@HerrCai0907 HerrCai0907 requested review from PiotrZSL and removed request for vbvictor March 14, 2025 13:13
Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but please wait for others to review

assert(v2.Captured() == &v2); // assertion failed.
}

Possible fixes include refactoring the function object into a class member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first possible fix is ​​to make the class completely uncopyable. In 90% of cases, the real reason for this warning is that someone forgot to disallow copying.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the rest of the users who really want the class to be copyable will refactor the code in the way you suggested.

@denzor200
Copy link

LGTM, please provide the change for one comment I left

…field-of-default-copyable-class' of github.com:HerrCai0907/llvm-project into 120863-clang-tidy-check-request-dont-capture-this-by-a-field-of-default-copyable-class
@HerrCai0907 HerrCai0907 merged commit 3b1e18c into llvm:main Mar 17, 2025
7 of 11 checks passed
@HerrCai0907 HerrCai0907 deleted the 120863-clang-tidy-check-request-dont-capture-this-by-a-field-of-default-copyable-class branch March 17, 2025 07:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang-tidy] Check request: don't capture this by a field of default copyable class
5 participants