Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangTidyModernizeModule LoopConvertCheck.cpp LoopConvertUtils.cpp + MakeUniqueCheck.cpp ModernizeTidyModule.cpp PassByValueCheck.cpp ReplaceAutoPtrCheck.cpp Index: clang-tidy/modernize/MakeUniqueCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/MakeUniqueCheck.h @@ -0,0 +1,41 @@ +//===--- MakeUniqueCheck.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_UNIQUE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// Replace the pattern: +/// \code +/// std::unique_ptr(new type(args...)) +/// \endcode +/// +/// With the C++14 version: +/// \code +/// std::make_unique(args...) +/// \endcode +class MakeUniqueCheck : public ClangTidyCheck { +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; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H + Index: clang-tidy/modernize/MakeUniqueCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/MakeUniqueCheck.cpp @@ -0,0 +1,112 @@ +//===--- MakeUniqueCheck.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 "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 { + +const char PointerType[] = "pointerType"; +const char ConstructorCall[] = "constructorCall"; +const char NewExpression[] = "newExpression"; + +/// \brief Returns the length of the token that goes since the beggining of the +/// constructor call until the '<' of the template. This token should either be +/// 'unique_ptr' or 'std::unique_ptr'. In any other case, it returns 0. +static int getTokenLength(SourceLocation TokenStart, const SourceManager &SM, + const LangOptions &LO) { + SmallVector Buffer; + bool Invalid = false; + StringRef Token = Lexer::getSpelling(TokenStart, Buffer, SM, LO, &Invalid); + + if (!Invalid && Token == "unique_ptr") + return Token.size(); + + if (!Invalid && Token == "std") { + Token = Lexer::getSpelling(TokenStart.getLocWithOffset(5), Buffer, SM, LO, + &Invalid); + if (!Invalid && Token == "unique_ptr") + return strlen("std::unique_ptr"); + } + return 0; +} + +void MakeUniqueCheck::registerMatchers(MatchFinder *Finder) { + if (getLangOpts().CPlusPlus11) { + Finder->addMatcher( + cxxBindTemporaryExpr(has( + cxxConstructExpr( + hasType(qualType(hasDeclaration(classTemplateSpecializationDecl( + matchesName("::std::unique_ptr"), + templateArgumentCountIs(1), + hasTemplateArgument( + 0, templateArgument( + refersToType(qualType().bind(PointerType)))))))), + argumentCountIs(1), + hasArgument(0, cxxNewExpr(hasType(pointsTo(qualType( + equalsBoundNode(PointerType))))) + .bind(NewExpression))) + .bind(ConstructorCall))), + this); + } +} + +void MakeUniqueCheck::check(const MatchFinder::MatchResult &Result) { + SourceManager &SM = *Result.SourceManager; + const auto *Construct = + Result.Nodes.getNodeAs(ConstructorCall); + const auto *New = Result.Nodes.getNodeAs(NewExpression); + + SourceLocation ConstructCallStart = Construct->getExprLoc(); + int Len = getTokenLength(ConstructCallStart, SM, LangOptions()); + if (Len == 0) + return; + SourceLocation ConstructCallEnd = + ConstructCallStart.getLocWithOffset(Len - 1); + + auto Diag = diag(ConstructCallStart, "use std::make_unique instead") + << FixItHint::CreateReplacement( + SourceRange(ConstructCallStart, ConstructCallEnd), + "std::make_unique"); + + 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: { + SourceRange InitRange = New->getInitializer()->getSourceRange(); + Diag << FixItHint::CreateRemoval( + SourceRange(NewStart, InitRange.getBegin().getLocWithOffset(-1))); + Diag << FixItHint::CreateRemoval( + SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd)); + break; + } + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "LoopConvertCheck.h" +#include "MakeUniqueCheck.h" #include "PassByValueCheck.h" #include "ReplaceAutoPtrCheck.h" #include "ShrinkToFitCheck.h" @@ -28,6 +29,8 @@ public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck("modernize-loop-convert"); + CheckFactories.registerCheck( + "modernize-make-unique"); CheckFactories.registerCheck("modernize-pass-by-value"); CheckFactories.registerCheck( "modernize-replace-auto-ptr"); Index: test/clang-tidy/modernize-make-unique.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-make-unique.cpp @@ -0,0 +1,99 @@ +// RUN: %python %S/check_clang_tidy.py %s modernize-make-unique %t + +namespace std { + +template +class unique_ptr { +public: + unique_ptr(type *ptr); + unique_ptr(const unique_ptr &t) = delete; + unique_ptr(unique_ptr &&t); + ~unique_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 Pair { + int a, b; +}; + +int g(std::unique_ptr P); + +std::unique_ptr getPointer() { + return std::unique_ptr(new Base); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use std::make_unique instead + // CHECK-FIXES: return std::make_unique(); +} + +void f() { + std::unique_ptr P1 = std::unique_ptr(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_unique instead [modernize-make-unique] + // CHECK-FIXES: std::unique_ptr P1 = std::make_unique(); + + // Without parenthesis. + std::unique_ptr P2 = std::unique_ptr(new int); + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_unique instead [modernize-make-unique] + // CHECK-FIXES: std::unique_ptr P2 = std::make_unique(); + + // With auto. + auto P3 = std::unique_ptr(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_unique instead + // CHECK-FIXES: auto P3 = std::make_unique(); + + { + // No std. + using namespace std; + unique_ptr Q = unique_ptr(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use std::make_unique instead + // CHECK-FIXES: unique_ptr Q = std::make_unique(); + } + + std::unique_ptr R(new int()); + + // Create the unique_ptr as a parameter to a function. + int T = g(std::unique_ptr(new int())); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_unique instead + // CHECK-FIXES: int T = g(std::make_unique()); + + // Arguments are correctly handled. + std::unique_ptr Pbase = std::unique_ptr(new Base(5, T)); + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use std::make_unique instead + // CHECK-FIXES: std::unique_ptr Pbase = std::make_unique(5, T); + + // Works with init lists. + std::unique_ptr Ppair = std::unique_ptr(new Pair{T, 1}); + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use std::make_unique instead + // CHECK-FIXES: std::unique_ptr Ppair = std::make_unique({T, 1}); + + // Only replace if the type in the template is the same than the type returned + // by the new operator. + auto Pderived = std::unique_ptr(new Derived()); + + // The pointer is returned by the function, nothing to do. + std::unique_ptr RetPtr = getPointer(); + + typedef std::unique_ptr IntPtr; + IntPtr Ipt = IntPtr(new int); + +#define PTR unique_ptr + std::unique_ptr Macro = std::PTR(new int); +#undef PTR +}