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+vbvic...@users.noreply.github.com> Co-authored-by: Baranov Victor <bar.victor.2...@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' +}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits