clang-tools 22.0.0git
CapturingThisInMemberVariableCheck.cpp
Go to the documentation of this file.
1//===--- CapturingThisInMemberVariableCheck.cpp - clang-tidy --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "../utils/Matchers.h"
12#include "clang/AST/DeclCXX.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/ASTMatchers/ASTMatchers.h"
15#include "clang/ASTMatchers/ASTMatchersMacros.h"
16
17using namespace clang::ast_matchers;
18
19namespace clang::tidy::bugprone {
20
21namespace {
22
23AST_MATCHER(CXXRecordDecl, correctHandleCaptureThisLambda) {
24 // unresolved
25 if (Node.needsOverloadResolutionForCopyConstructor() &&
26 Node.needsImplicitCopyConstructor())
27 return false;
28 if (Node.needsOverloadResolutionForMoveConstructor() &&
29 Node.needsImplicitMoveConstructor())
30 return false;
31 if (Node.needsOverloadResolutionForCopyAssignment() &&
32 Node.needsImplicitCopyAssignment())
33 return false;
34 if (Node.needsOverloadResolutionForMoveAssignment() &&
35 Node.needsImplicitMoveAssignment())
36 return false;
37 // default but not deleted
38 if (Node.hasSimpleCopyConstructor())
39 return false;
40 if (Node.hasSimpleMoveConstructor())
41 return false;
42 if (Node.hasSimpleCopyAssignment())
43 return false;
44 if (Node.hasSimpleMoveAssignment())
45 return false;
46
47 for (const CXXConstructorDecl *C : Node.ctors()) {
48 if (C->isCopyOrMoveConstructor() && C->isDefaulted() && !C->isDeleted())
49 return false;
50 }
51 for (const CXXMethodDecl *M : Node.methods()) {
52 if (M->isCopyAssignmentOperator())
53 llvm::errs() << M->isDeleted() << "\n";
54 if (M->isCopyAssignmentOperator() && M->isDefaulted() && !M->isDeleted())
55 return false;
56 if (M->isMoveAssignmentOperator() && M->isDefaulted() && !M->isDeleted())
57 return false;
58 }
59 // FIXME: find ways to identifier correct handle capture this lambda
60 return true;
61}
62
63} // namespace
64
65constexpr const char *DefaultFunctionWrapperTypes =
66 "::std::function;::std::move_only_function;::boost::function";
67constexpr const char *DefaultBindFunctions =
68 "::std::bind;::boost::bind;::std::bind_front;::std::bind_back;"
69 "::boost::compat::bind_front;::boost::compat::bind_back";
70
72 StringRef Name, ClangTidyContext *Context)
73 : ClangTidyCheck(Name, Context),
74 FunctionWrapperTypes(utils::options::parseStringList(
75 Options.get("FunctionWrapperTypes", DefaultFunctionWrapperTypes))),
76 BindFunctions(utils::options::parseStringList(
77 Options.get("BindFunctions", DefaultBindFunctions))) {}
80 Options.store(Opts, "FunctionWrapperTypes",
81 utils::options::serializeStringList(FunctionWrapperTypes));
82 Options.store(Opts, "BindFunctions",
84}
85
87 auto IsStdFunctionField =
88 fieldDecl(hasType(cxxRecordDecl(
89 matchers::matchesAnyListedName(FunctionWrapperTypes))))
90 .bind("field");
91 auto CaptureThis = lambdaCapture(anyOf(
92 // [this]
93 capturesThis(),
94 // [self = this]
95 capturesVar(varDecl(hasInitializer(cxxThisExpr())))));
96 auto IsLambdaCapturingThis =
97 lambdaExpr(hasAnyCapture(CaptureThis)).bind("lambda");
98
99 auto IsBindCapturingThis =
100 callExpr(
101 callee(functionDecl(matchers::matchesAnyListedName(BindFunctions))
102 .bind("callee")),
103 hasAnyArgument(cxxThisExpr()))
104 .bind("bind");
105
106 auto IsInitWithLambdaOrBind =
107 anyOf(IsLambdaCapturingThis, IsBindCapturingThis,
108 cxxConstructExpr(hasArgument(
109 0, anyOf(IsLambdaCapturingThis, IsBindCapturingThis))));
110
111 Finder->addMatcher(
112 cxxRecordDecl(
113 anyOf(has(cxxConstructorDecl(
114 unless(isCopyConstructor()), unless(isMoveConstructor()),
115 hasAnyConstructorInitializer(cxxCtorInitializer(
116 isMemberInitializer(), forField(IsStdFunctionField),
117 withInitializer(IsInitWithLambdaOrBind))))),
118 has(fieldDecl(IsStdFunctionField,
119 hasInClassInitializer(IsInitWithLambdaOrBind)))),
120 unless(correctHandleCaptureThisLambda())),
121 this);
122}
124 const MatchFinder::MatchResult &Result) {
125 if (const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda")) {
126 diag(Lambda->getBeginLoc(),
127 "'this' captured by a lambda and stored in a class member variable; "
128 "disable implicit class copying/moving to prevent potential "
129 "use-after-free");
130 } else if (const auto *Bind = Result.Nodes.getNodeAs<CallExpr>("bind")) {
131 const auto *Callee = Result.Nodes.getNodeAs<FunctionDecl>("callee");
132 assert(Callee);
133 diag(Bind->getBeginLoc(),
134 "'this' captured by a '%0' call and stored in a class member "
135 "variable; disable implicit class copying/moving to prevent potential "
136 "use-after-free")
137 << Callee->getQualifiedNameAsString();
138 }
139
140 const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field");
141 assert(Field);
142
143 diag(Field->getLocation(),
144 "class member of type '%0' that stores captured 'this'",
145 DiagnosticIDs::Note)
146 << Field->getType().getAsString();
147}
148
149} // namespace clang::tidy::bugprone
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
constexpr const char * DefaultFunctionWrapperTypes
AST_MATCHER(BinaryOperator, isRelationalOperator)
inline ::clang::ast_matchers::internal::Matcher< NamedDecl > matchesAnyListedName(llvm::ArrayRef< StringRef > NameList)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap