123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- //===--- MakeSmartPtrCheck.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 "../utils/TypeTraits.h"
- #include "MakeSharedCheck.h"
- #include "clang/Frontend/CompilerInstance.h"
- #include "clang/Lex/Lexer.h"
- #include "clang/Lex/Preprocessor.h"
- using namespace clang::ast_matchers;
- namespace clang::tidy::modernize {
- namespace {
- constexpr char ConstructorCall[] = "constructorCall";
- constexpr char ResetCall[] = "resetCall";
- constexpr char NewExpression[] = "newExpression";
- std::string getNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM,
- const LangOptions &Lang) {
- StringRef WrittenName = Lexer::getSourceText(
- CharSourceRange::getTokenRange(
- NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
- SM, Lang);
- if (NewExpr->isArray()) {
- return (WrittenName + "[]").str();
- }
- return WrittenName.str();
- }
- } // namespace
- const char MakeSmartPtrCheck::PointerType[] = "pointerType";
- MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
- StringRef MakeSmartPtrFunctionName)
- : ClangTidyCheck(Name, Context),
- Inserter(Options.getLocalOrGlobal("IncludeStyle",
- utils::IncludeSorter::IS_LLVM),
- areDiagsSelfContained()),
- MakeSmartPtrFunctionHeader(
- Options.get("MakeSmartPtrFunctionHeader", "<memory>")),
- MakeSmartPtrFunctionName(
- Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
- IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
- IgnoreDefaultInitialization(
- Options.get("IgnoreDefaultInitialization", true)) {}
- void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "IncludeStyle", Inserter.getStyle());
- Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
- Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
- Options.store(Opts, "IgnoreMacros", IgnoreMacros);
- Options.store(Opts, "IgnoreDefaultInitialization",
- IgnoreDefaultInitialization);
- }
- bool MakeSmartPtrCheck::isLanguageVersionSupported(
- const LangOptions &LangOpts) const {
- return LangOpts.CPlusPlus11;
- }
- void MakeSmartPtrCheck::registerPPCallbacks(const SourceManager &SM,
- Preprocessor *PP,
- Preprocessor *ModuleExpanderPP) {
- Inserter.registerPreprocessor(PP);
- }
- void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
- // Calling make_smart_ptr from within a member function of a type with a
- // private or protected constructor would be ill-formed.
- auto CanCallCtor = unless(has(ignoringImpCasts(
- cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
- auto IsPlacement = hasAnyPlacementArg(anything());
- Finder->addMatcher(
- traverse(
- TK_AsIs,
- cxxBindTemporaryExpr(has(ignoringParenImpCasts(
- cxxConstructExpr(
- hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
- hasArgument(
- 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
- equalsBoundNode(PointerType))))),
- CanCallCtor, unless(IsPlacement))
- .bind(NewExpression)),
- unless(isInTemplateInstantiation()))
- .bind(ConstructorCall))))),
- this);
- Finder->addMatcher(
- traverse(TK_AsIs,
- cxxMemberCallExpr(
- thisPointerType(getSmartPointerTypeMatcher()),
- callee(cxxMethodDecl(hasName("reset"))),
- hasArgument(0, cxxNewExpr(CanCallCtor, unless(IsPlacement))
- .bind(NewExpression)),
- unless(isInTemplateInstantiation()))
- .bind(ResetCall)),
- this);
- }
- void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
- // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
- // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
- // 'std::make_unique' or other function that creates smart_ptr.
- SourceManager &SM = *Result.SourceManager;
- const auto *Construct =
- Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
- const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
- const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
- const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
- // Skip when this is a new-expression with `auto`, e.g. new auto(1)
- if (New->getType()->getPointeeType()->getContainedAutoType())
- return;
- // Be conservative for cases where we construct and default initialize.
- //
- // For example,
- // P.reset(new int) // check fix: P = std::make_unique<int>()
- // P.reset(new int[5]) // check fix: P = std::make_unique<int []>(5)
- //
- // The fix of the check has side effect, it introduces value initialization
- // which maybe unexpected and cause performance regression.
- bool Initializes = New->hasInitializer() ||
- !utils::type_traits::isTriviallyDefaultConstructible(
- New->getAllocatedType(), *Result.Context);
- if (!Initializes && IgnoreDefaultInitialization)
- return;
- if (Construct)
- checkConstruct(SM, Result.Context, Construct, Type, New);
- else if (Reset)
- checkReset(SM, Result.Context, Reset, New);
- }
- void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx,
- const CXXConstructExpr *Construct,
- const QualType *Type,
- const CXXNewExpr *New) {
- SourceLocation ConstructCallStart = Construct->getExprLoc();
- bool InMacro = ConstructCallStart.isMacroID();
- if (InMacro && IgnoreMacros) {
- return;
- }
- bool Invalid = false;
- StringRef ExprStr = Lexer::getSourceText(
- CharSourceRange::getCharRange(
- ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
- SM, getLangOpts(), &Invalid);
- if (Invalid)
- return;
- auto Diag = diag(ConstructCallStart, "use %0 instead")
- << MakeSmartPtrFunctionName;
- // Disable the fix in macros.
- if (InMacro) {
- return;
- }
- if (!replaceNew(Diag, New, SM, Ctx)) {
- return;
- }
- // Find the location of the template's left angle.
- size_t LAngle = ExprStr.find('<');
- SourceLocation ConstructCallEnd;
- if (LAngle == StringRef::npos) {
- // If the template argument is missing (because it is part of the alias)
- // we have to add it back.
- ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
- Diag << FixItHint::CreateInsertion(
- ConstructCallEnd, "<" + getNewExprName(New, SM, getLangOpts()) + ">");
- } else {
- ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
- }
- Diag << FixItHint::CreateReplacement(
- CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
- MakeSmartPtrFunctionName);
- // If the smart_ptr is built with brace enclosed direct initialization, use
- // parenthesis instead.
- if (Construct->isListInitialization()) {
- SourceRange BraceRange = Construct->getParenOrBraceRange();
- Diag << FixItHint::CreateReplacement(
- CharSourceRange::getCharRange(
- BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
- "(");
- Diag << FixItHint::CreateReplacement(
- CharSourceRange::getCharRange(BraceRange.getEnd(),
- BraceRange.getEnd().getLocWithOffset(1)),
- ")");
- }
- insertHeader(Diag, SM.getFileID(ConstructCallStart));
- }
- void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
- const CXXMemberCallExpr *Reset,
- const CXXNewExpr *New) {
- const auto *Expr = cast<MemberExpr>(Reset->getCallee());
- SourceLocation OperatorLoc = Expr->getOperatorLoc();
- SourceLocation ResetCallStart = Reset->getExprLoc();
- SourceLocation ExprStart = Expr->getBeginLoc();
- SourceLocation ExprEnd =
- Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts());
- bool InMacro = ExprStart.isMacroID();
- if (InMacro && IgnoreMacros) {
- return;
- }
- // There are some cases where we don't have operator ("." or "->") of the
- // "reset" expression, e.g. call "reset()" method directly in the subclass of
- // "std::unique_ptr<>". We skip these cases.
- if (OperatorLoc.isInvalid()) {
- return;
- }
- auto Diag = diag(ResetCallStart, "use %0 instead")
- << MakeSmartPtrFunctionName;
- // Disable the fix in macros.
- if (InMacro) {
- return;
- }
- if (!replaceNew(Diag, New, SM, Ctx)) {
- return;
- }
- Diag << FixItHint::CreateReplacement(
- CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
- (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
- getNewExprName(New, SM, getLangOpts()) + ">")
- .str());
- if (Expr->isArrow())
- Diag << FixItHint::CreateInsertion(ExprStart, "*");
- insertHeader(Diag, SM.getFileID(OperatorLoc));
- }
- bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
- const CXXNewExpr *New, SourceManager &SM,
- ASTContext *Ctx) {
- auto SkipParensParents = [&](const Expr *E) {
- TraversalKindScope RAII(*Ctx, TK_AsIs);
- for (const Expr *OldE = nullptr; E != OldE;) {
- OldE = E;
- for (const auto &Node : Ctx->getParents(*E)) {
- if (const Expr *Parent = Node.get<ParenExpr>()) {
- E = Parent;
- break;
- }
- }
- }
- return E;
- };
- SourceRange NewRange = SkipParensParents(New)->getSourceRange();
- SourceLocation NewStart = NewRange.getBegin();
- SourceLocation NewEnd = NewRange.getEnd();
- // Skip when the source location of the new expression is invalid.
- if (NewStart.isInvalid() || NewEnd.isInvalid())
- return false;
- std::string ArraySizeExpr;
- if (const auto *ArraySize = New->getArraySize().value_or(nullptr)) {
- ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
- ArraySize->getSourceRange()),
- SM, getLangOpts())
- .str();
- }
- // Returns true if the given constructor expression has any braced-init-list
- // argument, e.g.
- // Foo({1, 2}, 1) => true
- // Foo(Bar{1, 2}) => true
- // Foo(1) => false
- // Foo{1} => false
- auto HasListIntializedArgument = [](const CXXConstructExpr *CE) {
- for (const auto *Arg : CE->arguments()) {
- Arg = Arg->IgnoreImplicit();
- if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
- return true;
- // Check whether we implicitly construct a class from a
- // std::initializer_list.
- if (const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
- // Strip the elidable move constructor, it is present in the AST for
- // C++11/14, e.g. Foo(Bar{1, 2}), the move constructor is around the
- // init-list constructor.
- if (CEArg->isElidable()) {
- if (const auto *TempExp = CEArg->getArg(0)) {
- if (const auto *UnwrappedCE =
- dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
- CEArg = UnwrappedCE;
- }
- }
- if (CEArg->isStdInitListInitialization())
- return true;
- }
- }
- return false;
- };
- switch (New->getInitializationStyle()) {
- case CXXNewExpr::NoInit: {
- if (ArraySizeExpr.empty()) {
- Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
- } else {
- // New array expression without written initializer:
- // smart_ptr<Foo[]>(new Foo[5]);
- Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
- ArraySizeExpr);
- }
- break;
- }
- case CXXNewExpr::CallInit: {
- // FIXME: Add fixes for constructors with parameters that can be created
- // with a C++11 braced-init-list (e.g. std::vector, std::map).
- // Unlike ordinal cases, braced list can not be deduced in
- // std::make_smart_ptr, we need to specify the type explicitly in the fixes:
- // struct S { S(std::initializer_list<int>, int); };
- // struct S2 { S2(std::vector<int>); };
- // struct S3 { S3(S2, int); };
- // smart_ptr<S>(new S({1, 2, 3}, 1)); // C++98 call-style initialization
- // smart_ptr<S>(new S({}, 1));
- // smart_ptr<S2>(new S2({1})); // implicit conversion:
- // // std::initializer_list => std::vector
- // smart_ptr<S3>(new S3({1, 2}, 3));
- // The above samples have to be replaced with:
- // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}), 1);
- // std::make_smart_ptr<S>(std::initializer_list<int>({}), 1);
- // std::make_smart_ptr<S2>(std::vector<int>({1}));
- // std::make_smart_ptr<S3>(S2{1, 2}, 3);
- if (const auto *CE = New->getConstructExpr()) {
- if (HasListIntializedArgument(CE))
- return false;
- }
- if (ArraySizeExpr.empty()) {
- SourceRange InitRange = New->getDirectInitRange();
- Diag << FixItHint::CreateRemoval(
- SourceRange(NewStart, InitRange.getBegin()));
- Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
- }
- else {
- // New array expression with default/value initialization:
- // smart_ptr<Foo[]>(new int[5]());
- // smart_ptr<Foo[]>(new Foo[5]());
- Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
- ArraySizeExpr);
- }
- break;
- }
- case CXXNewExpr::ListInit: {
- // Range of the substring that we do not want to remove.
- SourceRange InitRange;
- if (const auto *NewConstruct = New->getConstructExpr()) {
- if (NewConstruct->isStdInitListInitialization() ||
- HasListIntializedArgument(NewConstruct)) {
- // FIXME: Add fixes for direct initialization with the initializer-list
- // constructor. Similar to the above CallInit case, the type has to be
- // specified explicitly in the fixes.
- // struct S { S(std::initializer_list<int>); };
- // struct S2 { S2(S, int); };
- // smart_ptr<S>(new S{1, 2, 3}); // C++11 direct list-initialization
- // smart_ptr<S>(new S{}); // use initializer-list constructor
- // smart_ptr<S2>()new S2{ {1,2}, 3 }; // have a list-initialized arg
- // The above cases have to be replaced with:
- // std::make_smart_ptr<S>(std::initializer_list<int>({1, 2, 3}));
- // std::make_smart_ptr<S>(std::initializer_list<int>({}));
- // std::make_smart_ptr<S2>(S{1, 2}, 3);
- return false;
- }
- // Direct initialization with ordinary constructors.
- // struct S { S(int x); S(); };
- // smart_ptr<S>(new S{5});
- // smart_ptr<S>(new S{}); // use default constructor
- // The arguments in the initialization list are going to be forwarded to
- // the constructor, so this has to be replaced with:
- // std::make_smart_ptr<S>(5);
- // std::make_smart_ptr<S>();
- InitRange = SourceRange(
- NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
- NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
- } else {
- // Aggregate initialization.
- // smart_ptr<Pair>(new Pair{first, second});
- // Has to be replaced with:
- // smart_ptr<Pair>(Pair{first, second});
- //
- // The fix (std::make_unique) needs to see copy/move constructor of
- // Pair. If we found any invisible or deleted copy/move constructor, we
- // stop generating fixes -- as the C++ rule is complicated and we are less
- // certain about the correct fixes.
- if (const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {
- if (llvm::any_of(RD->ctors(), [](const CXXConstructorDecl *Ctor) {
- return Ctor->isCopyOrMoveConstructor() &&
- (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
- })) {
- return false;
- }
- }
- InitRange = SourceRange(
- New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
- New->getInitializer()->getSourceRange().getEnd());
- }
- Diag << FixItHint::CreateRemoval(
- CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
- Diag << FixItHint::CreateRemoval(
- SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
- break;
- }
- }
- return true;
- }
- void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
- if (MakeSmartPtrFunctionHeader.empty()) {
- return;
- }
- Diag << Inserter.createIncludeInsertion(FD, MakeSmartPtrFunctionHeader);
- }
- } // namespace clang::tidy::modernize
|