Prazek created this revision. Prazek added reviewers: alexfh, mnbvmar, staronj, krystyna, angelgarcia. Prazek added a subscriber: cfe-commits.
Because modernize-make-shared do almos the same job, I refactored common code to MakeSmartPtrCheck. http://reviews.llvm.org/D19183 Files: clang-tidy/modernize/CMakeLists.txt clang-tidy/modernize/MakeSharedCheck.cpp clang-tidy/modernize/MakeSharedCheck.h clang-tidy/modernize/MakeSmartPtrCheck.cpp clang-tidy/modernize/MakeSmartPtrCheck.h clang-tidy/modernize/MakeUniqueCheck.cpp clang-tidy/modernize/MakeUniqueCheck.h clang-tidy/modernize/ModernizeTidyModule.cpp docs/ReleaseNotes.rst docs/clang-tidy/checks/list.rst docs/clang-tidy/checks/modernize-make-shared.rst test/clang-tidy/modernize-make-shared.cpp
Index: test/clang-tidy/modernize-make-shared.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-make-shared.cpp @@ -0,0 +1,198 @@ +// RUN: %check_clang_tidy %s modernize-make-shared %t + +namespace std { + +template <typename type> +class shared_ptr { +public: + shared_ptr(type *ptr); + shared_ptr(const shared_ptr<type> &t) {} + shared_ptr(shared_ptr<type> &&t) {} + ~shared_ptr(); + type &operator*() { return *ptr; } + type *operator->() { return ptr; } + type *release(); + void reset(); + void reset(type *pt); + +private: + type *ptr; +}; +} + +struct Base { + Base(); + Base(int, int); +}; + +struct Derived : public Base { + Derived(); + Derived(int, int); +}; + +struct APair { + int a, b; +}; + +struct DPair { + DPair() : a(0), b(0) {} + DPair(int x, int y) : a(y), b(x) {} + int a, b; +}; + +struct Empty {}; + +template <class T> +using shared_ptr_ = std::shared_ptr<T>; + +void *operator new(__SIZE_TYPE__ Count, void *Ptr); + +int g(std::shared_ptr<int> P); + +std::shared_ptr<Base> getPointer() { + return std::shared_ptr<Base>(new Base); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use std::make_shared instead + // CHECK-FIXES: return std::make_shared<Base>(); +} + +void basic() { + std::shared_ptr<int> P1 = std::shared_ptr<int>(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_shared instead [modernize-make-shared] + // CHECK-FIXES: std::shared_ptr<int> P1 = std::make_shared<int>(); + + // Without parenthesis. + std::shared_ptr<int> P2 = std::shared_ptr<int>(new int); + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_shared instead [modernize-make-shared] + // CHECK-FIXES: std::shared_ptr<int> P2 = std::make_shared<int>(); + + // With auto. + auto P3 = std::shared_ptr<int>(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_shared instead + // CHECK-FIXES: auto P3 = std::make_shared<int>(); + + { + // No std. + using namespace std; + shared_ptr<int> Q = shared_ptr<int>(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use std::make_shared instead + // CHECK-FIXES: shared_ptr<int> Q = std::make_shared<int>(); + } + + std::shared_ptr<int> R(new int()); + + // Create the shared_ptr as a parameter to a function. + int T = g(std::shared_ptr<int>(new int())); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_shared instead + // CHECK-FIXES: int T = g(std::make_shared<int>()); + + // Only replace if the type in the template is the same than the type returned + // by the new operator. + auto Pderived = std::shared_ptr<Base>(new Derived()); + + // The pointer is returned by the function, nothing to do. + std::shared_ptr<Base> RetPtr = getPointer(); + + // This emulates std::move. + std::shared_ptr<int> Move = static_cast<std::shared_ptr<int> &&>(P1); + + // Placemenet arguments should not be removed. + int *PInt = new int; + std::shared_ptr<int> Placement = std::shared_ptr<int>(new (PInt) int{3}); +} + +void initialization(int T, Base b) { + // Test different kinds of initialization of the pointee. + + // Direct initialization with parenthesis. + std::shared_ptr<DPair> PDir1 = std::shared_ptr<DPair>(new DPair(1, T)); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<DPair> PDir1 = std::make_shared<DPair>(1, T); + + // Direct initialization with braces. + std::shared_ptr<DPair> PDir2 = std::shared_ptr<DPair>(new DPair{2, T}); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<DPair> PDir2 = std::make_shared<DPair>(2, T); + + // Aggregate initialization. + std::shared_ptr<APair> PAggr = std::shared_ptr<APair>(new APair{T, 1}); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<APair> PAggr = std::make_shared<APair>(APair{T, 1}); + + // Test different kinds of initialization of the pointee, when the shared_ptr + // is initialized with braces. + + // Direct initialization with parenthesis. + std::shared_ptr<DPair> PDir3 = std::shared_ptr<DPair>{new DPair(3, T)}; + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<DPair> PDir3 = std::make_shared<DPair>(3, T); + + // Direct initialization with braces. + std::shared_ptr<DPair> PDir4 = std::shared_ptr<DPair>{new DPair{4, T}}; + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<DPair> PDir4 = std::make_shared<DPair>(4, T); + + // Aggregate initialization. + std::shared_ptr<APair> PAggr2 = std::shared_ptr<APair>{new APair{T, 2}}; + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<APair> PAggr2 = std::make_shared<APair>(APair{T, 2}); + + // Direct initialization with parenthesis, without arguments. + std::shared_ptr<DPair> PDir5 = std::shared_ptr<DPair>(new DPair()); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<DPair> PDir5 = std::make_shared<DPair>(); + + // Direct initialization with braces, without arguments. + std::shared_ptr<DPair> PDir6 = std::shared_ptr<DPair>(new DPair{}); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<DPair> PDir6 = std::make_shared<DPair>(); + + // Aggregate initialization without arguments. + std::shared_ptr<Empty> PEmpty = std::shared_ptr<Empty>(new Empty{}); + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<Empty> PEmpty = std::make_shared<Empty>(Empty{}); +} + +void aliases() { + typedef std::shared_ptr<int> IntPtr; + IntPtr Typedef = IntPtr(new int); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use std::make_shared instead + // CHECK-FIXES: IntPtr Typedef = std::make_shared<int>(); + + // We use 'bool' instead of '_Bool'. + typedef std::shared_ptr<bool> BoolPtr; + BoolPtr BoolType = BoolPtr(new bool); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use std::make_shared instead + // CHECK-FIXES: BoolPtr BoolType = std::make_shared<bool>(); + + // We use 'Base' instead of 'struct Base'. + typedef std::shared_ptr<Base> BasePtr; + BasePtr StructType = BasePtr(new Base); +// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_shared instead +// CHECK-FIXES: BasePtr StructType = std::make_shared<Base>(); + +#define PTR shared_ptr<int> + std::shared_ptr<int> Macro = std::PTR(new int); +// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_shared instead +// CHECK-FIXES: std::shared_ptr<int> Macro = std::make_shared<int>(); +#undef PTR + + std::shared_ptr<int> Using = shared_ptr_<int>(new int); + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<int> Using = std::make_shared<int>(); +} + +void whitespaces() { + auto Space = std::shared_ptr <int>(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use std::make_shared instead + // CHECK-FIXES: auto Space = std::make_shared<int>(); + + auto Spaces = std :: shared_ptr <int>(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use std::make_shared instead + // CHECK-FIXES: auto Spaces = std::make_shared<int>(); +} + +void nesting() { + auto Nest = std::shared_ptr<std::shared_ptr<int>>(new std::shared_ptr<int>(new int)); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use std::make_shared instead + // CHECK-FIXES: auto Nest = std::make_shared<std::shared_ptr<int>>(new int); +} Index: docs/clang-tidy/checks/modernize-make-shared.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-make-shared.rst @@ -0,0 +1,16 @@ +.. title:: clang-tidy - modernize-make-shared + +modernize-make-shared +===================== + +This check finds the creation of ``std::shared_ptr`` objects by explicitly +calling the constructor and a ``new`` expression, and replaces it with a call +to ``std::make_shared``. + +.. code-block:: c++ + + auto my_ptr = std::shared_ptr<MyPair>(new MyPair(1, 2)); + + // becomes + + auto my_ptr = std::make_shared<MyPair>(1, 2); Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -31,9 +31,9 @@ google-build-using-namespace google-explicit-constructor google-global-names-in-headers - google-readability-braces-around-statements (redirects to readability-braces-around-statements) <readability-braces-around-statements> + google-readability-braces-around-statements (redirects to readability-braces-around-statements) <google-readability-braces-around-statements> google-readability-casting - google-readability-function-size (redirects to readability-function-size) <readability-function-size> + google-readability-function-size (redirects to readability-function-size) <google-readability-function-size> google-readability-namespace-comments google-readability-redundant-smartptr-get google-readability-todo @@ -78,6 +78,7 @@ misc-virtual-near-miss modernize-deprecated-headers modernize-loop-convert + modernize-make-shared modernize-make-unique modernize-pass-by-value modernize-raw-string-literal Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -113,6 +113,11 @@ Replaces C standard library headers with their C++ alternatives. +- New `modernize-make-shared + <http://clang.llvm.org/extra/clang-tidy/checks/modernize-make-shared.html>`_ check + + Replaces creation of ``std::shared_ptr`` from new expression with call to ``std::make_shared``. + - New `modernize-raw-string-literal <http://clang.llvm.org/extra/clang-tidy/checks/modernize-raw-string-literal.html>`_ check Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "DeprecatedHeadersCheck.h" #include "LoopConvertCheck.h" +#include "MakeSharedCheck.h" #include "MakeUniqueCheck.h" #include "PassByValueCheck.h" #include "RawStringLiteralCheck.h" @@ -35,6 +36,7 @@ CheckFactories.registerCheck<DeprecatedHeadersCheck>( "modernize-deprecated-headers"); CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert"); + CheckFactories.registerCheck<MakeSharedCheck>("modernize-make-shared"); CheckFactories.registerCheck<MakeUniqueCheck>("modernize-make-unique"); CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value"); CheckFactories.registerCheck<RawStringLiteralCheck>( Index: clang-tidy/modernize/MakeUniqueCheck.h =================================================================== --- clang-tidy/modernize/MakeUniqueCheck.h +++ clang-tidy/modernize/MakeUniqueCheck.h @@ -10,7 +10,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H -#include "../ClangTidy.h" +#include "MakeSmartPtrCheck.h" namespace clang { namespace tidy { @@ -25,12 +25,12 @@ /// \code /// std::make_unique<type>(args...) /// \endcode -class MakeUniqueCheck : public ClangTidyCheck { +class MakeUniqueCheck : public MakeSmartPtrCheck { public: - MakeUniqueCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + MakeUniqueCheck(StringRef Name, ClangTidyContext *Context); + +protected: + SmartPtrTypeMatcher getSmartPointerTypeMatcher() const override; }; } // namespace modernize Index: clang-tidy/modernize/MakeUniqueCheck.cpp =================================================================== --- clang-tidy/modernize/MakeUniqueCheck.cpp +++ clang-tidy/modernize/MakeUniqueCheck.cpp @@ -8,146 +8,31 @@ //===----------------------------------------------------------------------===// #include "MakeUniqueCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Lex/Lexer.h" using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace modernize { -static const char PointerType[] = "pointerType"; -static const char ConstructorCall[] = "constructorCall"; -static const char NewExpression[] = "newExpression"; - -void MakeUniqueCheck::registerMatchers(MatchFinder *Finder) { - if (getLangOpts().CPlusPlus11) { - Finder->addMatcher( - cxxBindTemporaryExpr(has( - cxxConstructExpr( - hasType(qualType(hasDeclaration(classTemplateSpecializationDecl( - matchesName("::std::unique_ptr"), - templateArgumentCountIs(2), - hasTemplateArgument(0, templateArgument(refersToType( - qualType().bind(PointerType)))), - hasTemplateArgument( - 1, templateArgument(refersToType(qualType( - hasDeclaration(classTemplateSpecializationDecl( - matchesName("::std::default_delete"), - templateArgumentCountIs(1), - hasTemplateArgument( - 0, templateArgument(refersToType( - qualType(equalsBoundNode( - PointerType))))))))))))))), - argumentCountIs(1), - hasArgument( - 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType( - equalsBoundNode(PointerType)))))) - .bind(NewExpression))) - .bind(ConstructorCall))), - this); - } -} - -void MakeUniqueCheck::check(const MatchFinder::MatchResult &Result) { - SourceManager &SM = *Result.SourceManager; - const auto *Construct = - Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall); - const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType); - const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression); - - if (New->getNumPlacementArgs() != 0) - return; - - SourceLocation ConstructCallStart = Construct->getExprLoc(); - - bool Invalid = false; - StringRef ExprStr = Lexer::getSourceText( - CharSourceRange::getCharRange( - ConstructCallStart, Construct->getParenOrBraceRange().getBegin()), - SM, LangOptions(), &Invalid); - if (Invalid) - return; - - auto Diag = diag(ConstructCallStart, "use std::make_unique instead"); - - // 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, "<" + Type->getAsString(getLangOpts()) + ">"); - } else { - ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle); - } - - Diag << FixItHint::CreateReplacement( - CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd), - "std::make_unique"); - - // If the unique_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)), - ")"); - } - - SourceLocation NewStart = New->getSourceRange().getBegin(); - SourceLocation NewEnd = New->getSourceRange().getEnd(); - switch (New->getInitializationStyle()) { - case CXXNewExpr::NoInit: { - Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd)); - break; - } - case CXXNewExpr::CallInit: { - SourceRange InitRange = New->getDirectInitRange(); - Diag << FixItHint::CreateRemoval( - SourceRange(NewStart, InitRange.getBegin())); - Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd)); - break; - } - case CXXNewExpr::ListInit: { - // Range of the substring that we do not want to remove. - SourceRange InitRange; - if (const auto *NewConstruct = New->getConstructExpr()) { - // Direct initialization with initialization list. - // struct S { S(int x) {} }; - // std::unique_ptr<S>(new S{5}); - // The arguments in the initialization list are going to be forwarded to - // the constructor, so this has to be replaced with: - // struct S { S(int x) {} }; - // std::make_unique<S>(5); - InitRange = SourceRange( - NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1), - NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1)); - } else { - // Aggregate initialization. - // std::unique_ptr<Pair>(new Pair{first, second}); - // Has to be replaced with: - // std::make_unique<Pair>(Pair{first, second}); - InitRange = SourceRange( - New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(), - New->getInitializer()->getSourceRange().getEnd()); - } - Diag << FixItHint::CreateRemoval( - CharSourceRange::getCharRange(NewStart, InitRange.getBegin())); - Diag << FixItHint::CreateRemoval( - SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd)); - break; - } - } +MakeUniqueCheck::MakeUniqueCheck(StringRef Name, + clang::tidy::ClangTidyContext *Context) + : MakeSmartPtrCheck(Name, Context, "std::make_unique") {} + +MakeUniqueCheck::SmartPtrTypeMatcher +MakeUniqueCheck::getSmartPointerTypeMatcher() const { + return qualType(hasDeclaration(classTemplateSpecializationDecl( + matchesName("::std::unique_ptr"), templateArgumentCountIs(2), + hasTemplateArgument( + 0, templateArgument(refersToType(qualType().bind(PointerType)))), + hasTemplateArgument( + 1, templateArgument(refersToType( + qualType(hasDeclaration(classTemplateSpecializationDecl( + matchesName("::std::default_delete"), + templateArgumentCountIs(1), + hasTemplateArgument( + 0, templateArgument(refersToType(qualType( + equalsBoundNode(PointerType)))))))))))))); } } // namespace modernize Index: clang-tidy/modernize/MakeSmartPtrCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/MakeSmartPtrCheck.h @@ -0,0 +1,50 @@ +//===--- MakeSmartPtrCheck.h - clang-tidy------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H + +#include "../ClangTidy.h" +#include <string> + +namespace clang { +namespace tidy { +namespace modernize { + +/// Base class for MakeSharedCheck and MakeUniqueCheck. +class MakeSmartPtrCheck : public ClangTidyCheck { +public: + MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context, + std::string makeSmartPtrFunctionName); + void registerMatchers(ast_matchers::MatchFinder *Finder) override final; + void + check(const ast_matchers::MatchFinder::MatchResult &Result) override final; + +protected: + using SmartPtrTypeMatcher = ast_matchers::internal::BindableMatcher<QualType>; + + /// Returns matcher that match with different smart pointer types. + /// + /// Requires to bind pointer type (qualType) with PointerType string declared + /// in this class. + virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const = 0; + + static const char PointerType[]; + static const char ConstructorCall[]; + static const char NewExpression[]; + +private: + std::string makeSmartPtrFunctionName; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H Index: clang-tidy/modernize/MakeSmartPtrCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/MakeSmartPtrCheck.cpp @@ -0,0 +1,153 @@ + +//===--- MakeSmartPtrCheck.cpp - clang-tidy--------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MakeSharedCheck.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +const char MakeSmartPtrCheck::PointerType[] = "pointerType"; +const char MakeSmartPtrCheck::ConstructorCall[] = "constructorCall"; +const char MakeSmartPtrCheck::NewExpression[] = "newExpression"; + +MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context, + std::string makeSmartPtrFunctionName) + : + + ClangTidyCheck(Name, Context), + makeSmartPtrFunctionName(std::move(makeSmartPtrFunctionName)) {} + +void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus11) + return; + + Finder->addMatcher( + cxxBindTemporaryExpr( + has(cxxConstructExpr( + hasType(getSmartPointerTypeMatcher()), argumentCountIs(1), + hasArgument( + 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType( + equalsBoundNode(PointerType)))))) + .bind(NewExpression))) + .bind(ConstructorCall))), + 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 *Type = Result.Nodes.getNodeAs<QualType>(PointerType); + const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression); + + if (New->getNumPlacementArgs() != 0) + return; + + SourceLocation ConstructCallStart = Construct->getExprLoc(); + + bool Invalid = false; + StringRef ExprStr = Lexer::getSourceText( + CharSourceRange::getCharRange( + ConstructCallStart, Construct->getParenOrBraceRange().getBegin()), + SM, LangOptions(), &Invalid); + if (Invalid) + return; + + auto Diag = diag(ConstructCallStart, "use %0 instead") + << makeSmartPtrFunctionName; + + // 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, "<" + Type->getAsString(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)), + ")"); + } + + SourceLocation NewStart = New->getSourceRange().getBegin(); + SourceLocation NewEnd = New->getSourceRange().getEnd(); + switch (New->getInitializationStyle()) { + case CXXNewExpr::NoInit: { + Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd)); + break; + } + case CXXNewExpr::CallInit: { + SourceRange InitRange = New->getDirectInitRange(); + Diag << FixItHint::CreateRemoval( + SourceRange(NewStart, InitRange.getBegin())); + Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd)); + break; + } + case CXXNewExpr::ListInit: { + // Range of the substring that we do not want to remove. + SourceRange InitRange; + if (const auto *NewConstruct = New->getConstructExpr()) { + // Direct initialization with initialization list. + // struct S { S(int x) {} }; + // smart_ptr<S>(new S{5}); + // The arguments in the initialization list are going to be forwarded to + // the constructor, so this has to be replaced with: + // struct S { S(int x) {} }; + // std::make_smart_ptr<S>(5); + 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}); + InitRange = SourceRange( + New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(), + New->getInitializer()->getSourceRange().getEnd()); + } + Diag << FixItHint::CreateRemoval( + CharSourceRange::getCharRange(NewStart, InitRange.getBegin())); + Diag << FixItHint::CreateRemoval( + SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd)); + break; + } + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: clang-tidy/modernize/MakeSharedCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/MakeSharedCheck.h @@ -0,0 +1,43 @@ +//===--- MakeSharedCheck.h - clang-tidy--------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H + +#include "MakeSmartPtrCheck.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// Replace the pattern: +/// \code +/// std::shared_ptr<type>(new type(args...)) +/// \endcode +/// +/// With the safer version: +/// \code +/// std::make_shared<type>(args...) +/// \endcode +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-make-shared.html +class MakeSharedCheck : public MakeSmartPtrCheck { +public: + MakeSharedCheck(StringRef Name, ClangTidyContext *Context); + +protected: + SmartPtrTypeMatcher getSmartPointerTypeMatcher() const override; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H Index: clang-tidy/modernize/MakeSharedCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/MakeSharedCheck.cpp @@ -0,0 +1,31 @@ +//===--- MakeSharedCheck.cpp - clang-tidy----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MakeSharedCheck.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +MakeSharedCheck::MakeSharedCheck(StringRef Name, ClangTidyContext *Context) + : MakeSmartPtrCheck(Name, Context, "std::make_shared") {} + +MakeSharedCheck::SmartPtrTypeMatcher +MakeSharedCheck::getSmartPointerTypeMatcher() const { + return qualType(hasDeclaration(classTemplateSpecializationDecl( + matchesName("::std::shared_ptr"), templateArgumentCountIs(1), + hasTemplateArgument( + 0, templateArgument(refersToType(qualType().bind(PointerType))))))); +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -4,6 +4,8 @@ DeprecatedHeadersCheck.cpp LoopConvertCheck.cpp LoopConvertUtils.cpp + MakeSmartPtrCheck.cpp + MakeSharedCheck.cpp MakeUniqueCheck.cpp ModernizeTidyModule.cpp PassByValueCheck.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits