================
@@ -0,0 +1,124 @@
+//===--- 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(),
+       "'%0' that stores captured 'this' and becomes invalid during "
----------------
vbvictor wrote:

nit: Maybe make this wording a bit more verbose if you wish
"class member of type '%0' that stores captured 'this' ..."

There is a chance that current wording can be hard to understand for some users 
with big classes.


https://github.com/llvm/llvm-project/pull/130297
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to