[clang-tools-extra] 3b1e18c - [clang-tidy] Add new check bugprone-capture-this-by-field (#130297)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 17 00:11:47 PDT 2025
Author: Congcong Cai
Date: 2025-03-17T15:11:43+08:00
New Revision: 3b1e18c2dba850922bc259a258e65490058e523d
URL: https://github.com/llvm/llvm-project/commit/3b1e18c2dba850922bc259a258e65490058e523d
DIFF: https://github.com/llvm/llvm-project/commit/3b1e18c2dba850922bc259a258e65490058e523d.diff
LOG: [clang-tidy] Add new check bugprone-capture-this-by-field (#130297)
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
---------
Co-authored-by: Baranov Victor <70346889+vbvictor at users.noreply.github.com>
Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
Added:
clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.cpp
clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.h
clang-tools-extra/docs/clang-tidy/checks/bugprone/capturing-this-in-member-variable.rst
clang-tools-extra/test/clang-tidy/checkers/bugprone/capturing-this-in-member-variable.cpp
Modified:
clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 0a3376949b6e5..b780a85bdf3fe 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 "CapturingThisInMemberVariableCheck.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<CapturingThisInMemberVariableCheck>(
+ "bugprone-capturing-this-in-member-variable");
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..e310ea9c94543 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
+ CapturingThisInMemberVariableCheck.cpp
CastingThroughVoidCheck.cpp
ChainedComparisonCheck.cpp
ComparePointerToMemberVirtualFunctionCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.cpp
new file mode 100644
index 0000000000000..add0576a42c33
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.cpp
@@ -0,0 +1,123 @@
+//===--- CapturingThisInMemberVariableCheck.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 "CapturingThisInMemberVariableCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.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())
+ llvm::errs() << M->isDeleted() << "\n";
+ 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
+
+constexpr const char *DefaultFunctionWrapperTypes =
+ "::std::function;::std::move_only_function;::boost::function";
+
+CapturingThisInMemberVariableCheck::CapturingThisInMemberVariableCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ FunctionWrapperTypes(utils::options::parseStringList(
+ Options.get("FunctionWrapperTypes", DefaultFunctionWrapperTypes))) {}
+void CapturingThisInMemberVariableCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "FunctionWrapperTypes",
+ utils::options::serializeStringList(FunctionWrapperTypes));
+}
+
+void CapturingThisInMemberVariableCheck::registerMatchers(MatchFinder *Finder) {
+ auto IsStdFunctionField =
+ fieldDecl(hasType(cxxRecordDecl(
+ matchers::matchesAnyListedName(FunctionWrapperTypes))))
+ .bind("field");
+ auto CaptureThis = lambdaCapture(anyOf(
+ // [this]
+ capturesThis(),
+ // [self = this]
+ capturesVar(varDecl(hasInitializer(cxxThisExpr())))));
+ auto IsLambdaCapturingThis =
+ lambdaExpr(hasAnyCapture(CaptureThis.bind("capture"))).bind("lambda");
+ auto IsInitWithLambda =
+ anyOf(IsLambdaCapturingThis,
+ cxxConstructExpr(hasArgument(0, IsLambdaCapturingThis)));
+ Finder->addMatcher(
+ cxxRecordDecl(
+ anyOf(has(cxxConstructorDecl(
+ unless(isCopyConstructor()), unless(isMoveConstructor()),
+ hasAnyConstructorInitializer(cxxCtorInitializer(
+ isMemberInitializer(), forField(IsStdFunctionField),
+ withInitializer(IsInitWithLambda))))),
+ has(fieldDecl(IsStdFunctionField,
+ hasInClassInitializer(IsInitWithLambda)))),
+ unless(correctHandleCaptureThisLambda())),
+ this);
+}
+
+void CapturingThisInMemberVariableCheck::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(),
+ "'this' captured by a lambda and stored in a class member variable; "
+ "disable implicit class copying/moving to prevent potential "
+ "use-after-free")
+ << Capture->getLocation();
+ diag(Field->getLocation(),
+ "class member of type '%0' that stores captured 'this'",
+ DiagnosticIDs::Note)
+ << Field->getType().getAsString();
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.h b/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.h
new file mode 100644
index 0000000000000..fe0b0aa10f108
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.h
@@ -0,0 +1,44 @@
+//===--- CapturingThisInMemberVariableCheck.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_CAPTURINGTHISINMEMBERVARIABLECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CAPTURINGTHISINMEMBERVARIABLECHECK_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/capturing-this-in-member-variable.html
+class CapturingThisInMemberVariableCheck : public ClangTidyCheck {
+public:
+ CapturingThisInMemberVariableCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ 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;
+ }
+
+private:
+ ///< store the function wrapper types
+ const std::vector<StringRef> FunctionWrapperTypes;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CAPTURINGTHISINMEMBERVARIABLECHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index eaf37e746050e..2252efb498c2c 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -100,6 +100,12 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^
+- New :doc:`bugprone-capturing-this-in-member-variable
+ <clang-tidy/checks/bugprone/capturing-this-in-member-variable>` 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/capturing-this-in-member-variable.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/capturing-this-in-member-variable.rst
new file mode 100644
index 0000000000000..bb75e9239d9b5
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/capturing-this-in-member-variable.rst
@@ -0,0 +1,39 @@
+.. title:: clang-tidy - bugprone-capturing-this-in-member-variable
+
+bugprone-capturing-this-in-member-variable
+==========================================
+
+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.
+ }
+
+Possible fixes:
+ - marking copy and move constructors and assignment operators deleted.
+ - using class member method instead of class member variable with function
+ object types.
+ - passing ``this`` pointer as parameter
+
+.. option:: FunctionWrapperTypes
+
+ A semicolon-separated list of names of types. Used to specify function
+ wrapper that can hold lambda expressions.
+ Default is `::std::function;::std::move_only_function;::boost::function`.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index c73bc8bff3539..18f1467285fab 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-capturing-this-in-member-variable <bugprone/capturing-this-in-member-variable>`,
: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/capturing-this-in-member-variable.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/capturing-this-in-member-variable.cpp
new file mode 100644
index 0000000000000..f5ebebfe4b058
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/capturing-this-in-member-variable.cpp
@@ -0,0 +1,210 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s bugprone-capturing-this-in-member-variable %t -- -config="{CheckOptions: {bugprone-capturing-this-in-member-variable.FunctionWrapperTypes: '::std::function;::Fn'}}" --
+
+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 Fn {
+ template<class F> Fn(F &&);
+};
+
+struct BasicConstructor {
+ BasicConstructor() : Captured([this]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: 'this' captured by a lambda and stored in a class member variable;
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct BasicConstructorWithCaptureAllByValue {
+ BasicConstructorWithCaptureAllByValue() : Captured([=]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: 'this' captured by a lambda and stored in a class member variable;
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct BasicConstructorWithCaptureAllByRef {
+ BasicConstructorWithCaptureAllByRef() : Captured([&]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:52: warning: 'this' captured by a lambda and stored in a class member variable;
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct BasicField1 {
+ std::function<void()> Captured = [this]() { static_cast<void>(this); };
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: 'this' captured by a lambda and stored in a class member variable;
+ // CHECK-MESSAGES: :[[@LINE-2]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+struct BasicField2 {
+ std::function<void()> Captured{[this]() { static_cast<void>(this); }};
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: 'this' captured by a lambda and stored in a class member variable;
+ // CHECK-MESSAGES: :[[@LINE-2]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct NotCaptureThis {
+ NotCaptureThis(int V) : Captured([V]() { static_cast<void>(V); }) {}
+ std::function<void()> Captured;
+};
+
+struct AssignCapture {
+ AssignCapture() : Captured([Self = this]() { static_cast<void>(Self); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: 'this' captured by a lambda and stored in a class member variable;
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' 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: 'this' captured by a lambda and stored in a class member variable;
+ 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: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct DeleteMoveDefaultCopy {
+ DeleteMoveDefaultCopy() : Captured([this]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: 'this' captured by a lambda and stored in a class member variable;
+ 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: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct DeleteCopyBase {
+ DeleteCopyBase() = default;
+ DeleteCopyBase(DeleteCopyBase const&) = delete;
+ DeleteCopyBase(DeleteCopyBase &&) = default;
+ DeleteCopyBase& operator=(DeleteCopyBase const&) = delete;
+ DeleteCopyBase& operator=(DeleteCopyBase &&) = default;
+};
+
+struct DeleteMoveBase {
+ DeleteMoveBase() = default;
+ DeleteMoveBase(DeleteMoveBase const&) = default;
+ DeleteMoveBase(DeleteMoveBase &&) = delete;
+ DeleteMoveBase& operator=(DeleteMoveBase const&) = default;
+ DeleteMoveBase& operator=(DeleteMoveBase &&) = delete;
+};
+
+struct DeleteCopyMoveBase : DeleteCopyBase, DeleteMoveBase {};
+
+struct InheritDeleteCopy : DeleteCopyBase {
+ InheritDeleteCopy() : DeleteCopyBase{}, Captured([this]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:52: warning: 'this' captured by a lambda and stored in a class member variable;
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+struct InheritDeleteMove : DeleteMoveBase {
+ InheritDeleteMove() : DeleteMoveBase{}, Captured([this]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:52: warning: 'this' captured by a lambda and stored in a class member variable;
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+struct InheritDeleteCopyMove : DeleteCopyMoveBase {
+ InheritDeleteCopyMove() : DeleteCopyMoveBase{}, Captured([this]() { static_cast<void>(this); }) {}
+ std::function<void()> Captured;
+};
+
+struct PrivateCopyMoveBase {
+// It is how to disable copy and move in C++03
+ PrivateCopyMoveBase() = default;
+private:
+ PrivateCopyMoveBase(PrivateCopyMoveBase const&) = default;
+ PrivateCopyMoveBase(PrivateCopyMoveBase &&) = default;
+ PrivateCopyMoveBase& operator=(PrivateCopyMoveBase const&) = default;
+ PrivateCopyMoveBase& operator=(PrivateCopyMoveBase &&) = default;
+};
+struct InheritPrivateCopyMove : PrivateCopyMoveBase {
+ InheritPrivateCopyMove() : PrivateCopyMoveBase{}, 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: 'this' captured by a lambda and stored in a class member variable;
+ UserDefinedCopyMoveWithDefault1(UserDefinedCopyMoveWithDefault1 const&) = default;
+ UserDefinedCopyMoveWithDefault1(UserDefinedCopyMoveWithDefault1 &&);
+ UserDefinedCopyMoveWithDefault1& operator=(UserDefinedCopyMoveWithDefault1 const&);
+ UserDefinedCopyMoveWithDefault1& operator=(UserDefinedCopyMoveWithDefault1 &&);
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct UserDefinedCopyMoveWithDefault2 {
+ UserDefinedCopyMoveWithDefault2() : Captured([this]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: 'this' captured by a lambda and stored in a class member variable;
+ UserDefinedCopyMoveWithDefault2(UserDefinedCopyMoveWithDefault2 const&);
+ UserDefinedCopyMoveWithDefault2(UserDefinedCopyMoveWithDefault2 &&) = default;
+ UserDefinedCopyMoveWithDefault2& operator=(UserDefinedCopyMoveWithDefault2 const&);
+ UserDefinedCopyMoveWithDefault2& operator=(UserDefinedCopyMoveWithDefault2 &&);
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct UserDefinedCopyMoveWithDefault3 {
+ UserDefinedCopyMoveWithDefault3() : Captured([this]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: 'this' captured by a lambda and stored in a class member variable;
+ UserDefinedCopyMoveWithDefault3(UserDefinedCopyMoveWithDefault3 const&);
+ UserDefinedCopyMoveWithDefault3(UserDefinedCopyMoveWithDefault3 &&);
+ UserDefinedCopyMoveWithDefault3& operator=(UserDefinedCopyMoveWithDefault3 const&) = default;
+ UserDefinedCopyMoveWithDefault3& operator=(UserDefinedCopyMoveWithDefault3 &&);
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct UserDefinedCopyMoveWithDefault4 {
+ UserDefinedCopyMoveWithDefault4() : Captured([this]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: 'this' captured by a lambda and stored in a class member variable;
+ UserDefinedCopyMoveWithDefault4(UserDefinedCopyMoveWithDefault4 const&);
+ UserDefinedCopyMoveWithDefault4(UserDefinedCopyMoveWithDefault4 &&);
+ UserDefinedCopyMoveWithDefault4& operator=(UserDefinedCopyMoveWithDefault4 const&);
+ UserDefinedCopyMoveWithDefault4& operator=(UserDefinedCopyMoveWithDefault4 &&) = default;
+ std::function<void()> Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: note: class member of type 'std::function<void (void)>' that stores captured 'this'
+};
+
+struct CustomFunctionWrapper {
+ CustomFunctionWrapper() : Captured([this]() { static_cast<void>(this); }) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: 'this' captured by a lambda and stored in a class member variable;
+ Fn Captured;
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: note: class member of type 'Fn' that stores captured 'this'
+};
More information about the cfe-commits
mailing list