diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp')
-rw-r--r-- | contrib/libs/clang16/tools/extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp new file mode 100644 index 0000000000..2f9f47d3f6 --- /dev/null +++ b/contrib/libs/clang16/tools/extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp @@ -0,0 +1,440 @@ +//===--- 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 |