brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.7 KiB · 7940939 Raw
449 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 "../utils/TypeTraits.h"10#include "MakeSharedCheck.h"11#include "clang/Frontend/CompilerInstance.h"12#include "clang/Lex/Lexer.h"13#include "clang/Lex/Preprocessor.h"14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::modernize {18 19static constexpr char ConstructorCall[] = "constructorCall";20static constexpr char DirectVar[] = "directVar";21static constexpr char ResetCall[] = "resetCall";22static constexpr char NewExpression[] = "newExpression";23 24static std::string getNewExprName(const CXXNewExpr *NewExpr,25                                  const SourceManager &SM,26                                  const LangOptions &Lang) {27  const StringRef WrittenName = Lexer::getSourceText(28      CharSourceRange::getTokenRange(29          NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),30      SM, Lang);31  if (NewExpr->isArray()) {32    return (WrittenName + "[]").str();33  }34  return WrittenName.str();35}36 37const char MakeSmartPtrCheck::PointerType[] = "pointerType";38 39MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,40                                     StringRef MakeSmartPtrFunctionName)41    : ClangTidyCheck(Name, Context),42      Inserter(Options.getLocalOrGlobal("IncludeStyle",43                                        utils::IncludeSorter::IS_LLVM),44               areDiagsSelfContained()),45      MakeSmartPtrFunctionHeader(46          Options.get("MakeSmartPtrFunctionHeader", "<memory>")),47      MakeSmartPtrFunctionName(48          Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)),49      IgnoreMacros(Options.get("IgnoreMacros", true)),50      IgnoreDefaultInitialization(51          Options.get("IgnoreDefaultInitialization", true)) {}52 53void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {54  Options.store(Opts, "IncludeStyle", Inserter.getStyle());55  Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);56  Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);57  Options.store(Opts, "IgnoreMacros", IgnoreMacros);58  Options.store(Opts, "IgnoreDefaultInitialization",59                IgnoreDefaultInitialization);60}61 62bool MakeSmartPtrCheck::isLanguageVersionSupported(63    const LangOptions &LangOpts) const {64  return LangOpts.CPlusPlus11;65}66 67void MakeSmartPtrCheck::registerPPCallbacks(const SourceManager &SM,68                                            Preprocessor *PP,69                                            Preprocessor *ModuleExpanderPP) {70  Inserter.registerPreprocessor(PP);71}72 73void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {74  // Calling make_smart_ptr from within a member function of a type with a75  // private or protected constructor would be ill-formed.76  auto CanCallCtor = unless(has(ignoringImpCasts(77      cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));78 79  auto IsPlacement = hasAnyPlacementArg(anything());80 81  Finder->addMatcher(82      traverse(TK_AsIs,83               cxxConstructExpr(84                   anyOf(hasParent(cxxBindTemporaryExpr()),85                         hasParent(varDecl().bind(DirectVar))),86                   hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),87                   hasArgument(88                       0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(89                                         equalsBoundNode(PointerType))))),90                                     CanCallCtor, unless(IsPlacement))91                              .bind(NewExpression)),92                   unless(isInTemplateInstantiation()))93                   .bind(ConstructorCall)),94      this);95 96  Finder->addMatcher(97      traverse(98          TK_AsIs,99          cxxMemberCallExpr(100              unless(isInTemplateInstantiation()),101              hasArgument(0, cxxNewExpr(CanCallCtor, unless(IsPlacement))102                                 .bind(NewExpression)),103              callee(cxxMethodDecl(hasName("reset"))),104              anyOf(thisPointerType(getSmartPointerTypeMatcher()),105                    on(ignoringImplicit(anyOf(106                        hasType(getSmartPointerTypeMatcher()),107                        hasType(pointsTo(getSmartPointerTypeMatcher())))))))108              .bind(ResetCall)),109      this);110}111 112void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {113  // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other114  // pointer, 'make_smart_ptr' refers to 'std::make_shared' or115  // 'std::make_unique' or other function that creates smart_ptr.116 117  SourceManager &SM = *Result.SourceManager;118  const auto *Construct =119      Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);120  const auto *DVar = Result.Nodes.getNodeAs<VarDecl>(DirectVar);121  const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);122  const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);123  const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);124 125  // Skip when this is a new-expression with `auto`, e.g. new auto(1)126  if (New->getType()->getPointeeType()->getContainedAutoType())127    return;128 129  // Be conservative for cases where we construct and default initialize.130  //131  // For example,132  //    P.reset(new int)    // check fix: P = std::make_unique<int>()133  //    P.reset(new int[5]) // check fix: P = std::make_unique<int []>(5)134  //135  // The fix of the check has side effect, it introduces value initialization136  // which maybe unexpected and cause performance regression.137  const bool Initializes = New->hasInitializer() ||138                           !utils::type_traits::isTriviallyDefaultConstructible(139                               New->getAllocatedType(), *Result.Context);140  if (!Initializes && IgnoreDefaultInitialization)141    return;142  if (Construct)143    checkConstruct(SM, Result.Context, Construct, DVar, Type, New);144  else if (Reset)145    checkReset(SM, Result.Context, Reset, New);146}147 148void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx,149                                       const CXXConstructExpr *Construct,150                                       const VarDecl *DVar,151                                       const QualType *Type,152                                       const CXXNewExpr *New) {153  const SourceLocation ConstructCallStart = Construct->getExprLoc();154  const bool InMacro = ConstructCallStart.isMacroID();155 156  if (InMacro && IgnoreMacros) {157    return;158  }159 160  bool Invalid = false;161  const StringRef ExprStr = Lexer::getSourceText(162      CharSourceRange::getCharRange(163          ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),164      SM, getLangOpts(), &Invalid);165  if (Invalid)166    return;167 168  auto Diag = diag(ConstructCallStart, "use %0 instead")169              << MakeSmartPtrFunctionName;170 171  // Disable the fix in macros.172  if (InMacro) {173    return;174  }175 176  if (!replaceNew(Diag, New, SM, Ctx)) {177    return;178  }179 180  // Find the location of the template's left angle.181  const size_t LAngle = ExprStr.find('<');182  SourceLocation ConstructCallEnd;183  if (LAngle == StringRef::npos) {184    // If the template argument is missing (because it is part of the alias)185    // we have to add it back.186    ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());187    Diag << FixItHint::CreateInsertion(188        ConstructCallEnd, "<" + getNewExprName(New, SM, getLangOpts()) + ">");189  } else {190    ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);191  }192 193  std::string FinalMakeSmartPtrFunctionName = MakeSmartPtrFunctionName.str();194  if (DVar)195    FinalMakeSmartPtrFunctionName =196        ExprStr.str() + " = " + MakeSmartPtrFunctionName.str();197 198  Diag << FixItHint::CreateReplacement(199      CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),200      FinalMakeSmartPtrFunctionName);201 202  // If the smart_ptr is built with brace enclosed direct initialization, use203  // parenthesis instead.204  if (Construct->isListInitialization()) {205    const SourceRange BraceRange = Construct->getParenOrBraceRange();206    Diag << FixItHint::CreateReplacement(207        CharSourceRange::getCharRange(208            BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),209        "(");210    Diag << FixItHint::CreateReplacement(211        CharSourceRange::getCharRange(BraceRange.getEnd(),212                                      BraceRange.getEnd().getLocWithOffset(1)),213        ")");214  }215 216  insertHeader(Diag, SM.getFileID(ConstructCallStart));217}218 219void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,220                                   const CXXMemberCallExpr *Reset,221                                   const CXXNewExpr *New) {222  const auto *Expr = cast<MemberExpr>(Reset->getCallee());223  const SourceLocation OperatorLoc = Expr->getOperatorLoc();224  const SourceLocation ResetCallStart = Reset->getExprLoc();225  const SourceLocation ExprStart = Expr->getBeginLoc();226  const SourceLocation ExprEnd =227      Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts());228 229  const bool InMacro = ExprStart.isMacroID();230 231  if (InMacro && IgnoreMacros) {232    return;233  }234 235  // There are some cases where we don't have operator ("." or "->") of the236  // "reset" expression, e.g. call "reset()" method directly in the subclass of237  // "std::unique_ptr<>". We skip these cases.238  if (OperatorLoc.isInvalid()) {239    return;240  }241 242  auto Diag = diag(ResetCallStart, "use %0 instead")243              << MakeSmartPtrFunctionName;244 245  // Disable the fix in macros.246  if (InMacro) {247    return;248  }249 250  if (!replaceNew(Diag, New, SM, Ctx)) {251    return;252  }253 254  Diag << FixItHint::CreateReplacement(255      CharSourceRange::getCharRange(OperatorLoc, ExprEnd),256      (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +257       getNewExprName(New, SM, getLangOpts()) + ">")258          .str());259 260  if (Expr->isArrow())261    Diag << FixItHint::CreateInsertion(ExprStart, "*");262 263  insertHeader(Diag, SM.getFileID(OperatorLoc));264}265 266bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,267                                   const CXXNewExpr *New, SourceManager &SM,268                                   ASTContext *Ctx) {269  auto SkipParensParents = [&](const Expr *E) {270    const TraversalKindScope RAII(*Ctx, TK_AsIs);271 272    for (const Expr *OldE = nullptr; E != OldE;) {273      OldE = E;274      for (const auto &Node : Ctx->getParents(*E)) {275        if (const Expr *Parent = Node.get<ParenExpr>()) {276          E = Parent;277          break;278        }279      }280    }281    return E;282  };283 284  const SourceRange NewRange = SkipParensParents(New)->getSourceRange();285  const SourceLocation NewStart = NewRange.getBegin();286  const SourceLocation NewEnd = NewRange.getEnd();287 288  // Skip when the source location of the new expression is invalid.289  if (NewStart.isInvalid() || NewEnd.isInvalid())290    return false;291 292  std::string ArraySizeExpr;293  if (const auto *ArraySize = New->getArraySize().value_or(nullptr)) {294    ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(295                                             ArraySize->getSourceRange()),296                                         SM, getLangOpts())297                        .str();298  }299  // Returns true if the given constructor expression has any braced-init-list300  // argument, e.g.301  //   Foo({1, 2}, 1) => true302  //   Foo(Bar{1, 2}) => true303  //   Foo(1) => false304  //   Foo{1} => false305  auto HasListIntializedArgument = [](const CXXConstructExpr *CE) {306    for (const auto *Arg : CE->arguments()) {307      Arg = Arg->IgnoreImplicit();308 309      if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))310        return true;311      // Check whether we implicitly construct a class from a312      // std::initializer_list.313      if (const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {314        // Strip the elidable move constructor, it is present in the AST for315        // C++11/14, e.g. Foo(Bar{1, 2}), the move constructor is around the316        // init-list constructor.317        if (CEArg->isElidable()) {318          if (const auto *TempExp = CEArg->getArg(0)) {319            if (const auto *UnwrappedCE =320                    dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))321              CEArg = UnwrappedCE;322          }323        }324        if (CEArg->isStdInitListInitialization())325          return true;326      }327    }328    return false;329  };330  switch (New->getInitializationStyle()) {331  case CXXNewInitializationStyle::None: {332    if (ArraySizeExpr.empty()) {333      Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));334    } else {335      // New array expression without written initializer:336      //   smart_ptr<Foo[]>(new Foo[5]);337      Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),338                                           ArraySizeExpr);339    }340    break;341  }342  case CXXNewInitializationStyle::Parens: {343    // FIXME: Add fixes for constructors with parameters that can be created344    // with a C++11 braced-init-list (e.g. std::vector, std::map).345    // Unlike ordinal cases, braced list can not be deduced in346    // std::make_smart_ptr, we need to specify the type explicitly in the fixes:347    //   struct S { S(std::initializer_list<int>, int); };348    //   struct S2 { S2(std::vector<int>); };349    //   struct S3 { S3(S2, int); };350    //   smart_ptr<S>(new S({1, 2, 3}, 1));  // C++98 call-style initialization351    //   smart_ptr<S>(new S({}, 1));352    //   smart_ptr<S2>(new S2({1})); // implicit conversion:353    //                               //   std::initializer_list => std::vector354    //   smart_ptr<S3>(new S3({1, 2}, 3));355    // The above samples have to be replaced with:356    //   std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);357    //   std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);358    //   std::make_smart_ptr<S2>(std::vector<int>({1}));359    //   std::make_smart_ptr<S3>(S2{1, 2}, 3);360    if (const auto *CE = New->getConstructExpr()) {361      if (HasListIntializedArgument(CE))362        return false;363    }364    if (ArraySizeExpr.empty()) {365      const SourceRange InitRange = New->getDirectInitRange();366      Diag << FixItHint::CreateRemoval(367          SourceRange(NewStart, InitRange.getBegin()));368      Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));369    } else {370      // New array expression with default/value initialization:371      //   smart_ptr<Foo[]>(new int[5]());372      //   smart_ptr<Foo[]>(new Foo[5]());373      Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),374                                           ArraySizeExpr);375    }376    break;377  }378  case CXXNewInitializationStyle::Braces: {379    // Range of the substring that we do not want to remove.380    SourceRange InitRange;381    if (const auto *NewConstruct = New->getConstructExpr()) {382      if (NewConstruct->isStdInitListInitialization() ||383          HasListIntializedArgument(NewConstruct)) {384        // FIXME: Add fixes for direct initialization with the initializer-list385        // constructor. Similar to the above CallInit case, the type has to be386        // specified explicitly in the fixes.387        //   struct S { S(std::initializer_list<int>); };388        //   struct S2 { S2(S, int); };389        //   smart_ptr<S>(new S{1, 2, 3});  // C++11 direct list-initialization390        //   smart_ptr<S>(new S{});  // use initializer-list constructor391        //   smart_ptr<S2>()new S2{ {1,2}, 3 }; // have a list-initialized arg392        // The above cases have to be replaced with:393        //   std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));394        //   std::make_smart_ptr<S>(std::initializer_list<int>({}));395        //   std::make_smart_ptr<S2>(S{1, 2}, 3);396        return false;397      }398      // Direct initialization with ordinary constructors.399      //   struct S { S(int x); S(); };400      //   smart_ptr<S>(new S{5});401      //   smart_ptr<S>(new S{}); // use default constructor402      // The arguments in the initialization list are going to be forwarded to403      // the constructor, so this has to be replaced with:404      //   std::make_smart_ptr<S>(5);405      //   std::make_smart_ptr<S>();406      InitRange = SourceRange(407          NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),408          NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));409    } else {410      // Aggregate initialization.411      //   smart_ptr<Pair>(new Pair{first, second});412      // Has to be replaced with:413      //   smart_ptr<Pair>(Pair{first, second});414      //415      // The fix (std::make_unique) needs to see copy/move constructor of416      // Pair. If we found any invisible or deleted copy/move constructor, we417      // stop generating fixes -- as the C++ rule is complicated and we are less418      // certain about the correct fixes.419      if (const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {420        if (llvm::any_of(RD->ctors(), [](const CXXConstructorDecl *Ctor) {421              return Ctor->isCopyOrMoveConstructor() &&422                     (Ctor->isDeleted() || Ctor->getAccess() == AS_private);423            })) {424          return false;425        }426      }427      InitRange = SourceRange(428          New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),429          New->getInitializer()->getSourceRange().getEnd());430    }431    Diag << FixItHint::CreateRemoval(432        CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));433    Diag << FixItHint::CreateRemoval(434        SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));435    break;436  }437  }438  return true;439}440 441void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {442  if (MakeSmartPtrFunctionHeader.empty()) {443    return;444  }445  Diag << Inserter.createIncludeInsertion(FD, MakeSmartPtrFunctionHeader);446}447 448} // namespace clang::tidy::modernize449