brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.6 KiB · a376de5 Raw
150 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "CapturingThisInMemberVariableCheck.h"10#include "../utils/Matchers.h"11#include "../utils/OptionsUtils.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  // unresolved25  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 deleted38  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 lambda60  return true;61}62 63} // namespace64 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 71CapturingThisInMemberVariableCheck::CapturingThisInMemberVariableCheck(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))) {}78void CapturingThisInMemberVariableCheck::storeOptions(79    ClangTidyOptions::OptionMap &Opts) {80  Options.store(Opts, "FunctionWrapperTypes",81                utils::options::serializeStringList(FunctionWrapperTypes));82  Options.store(Opts, "BindFunctions",83                utils::options::serializeStringList(BindFunctions));84}85 86void CapturingThisInMemberVariableCheck::registerMatchers(MatchFinder *Finder) {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}123void CapturingThisInMemberVariableCheck::check(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::bugprone150