blob: b7c630c87b5e35c3f1b7975c6c28f771362c550b [file] [log] [blame]
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +00001//===--- MakeUniqueCheck.cpp - clang-tidy----------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "MakeUniqueCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14
15using namespace clang::ast_matchers;
16
17namespace clang {
18namespace tidy {
19namespace modernize {
20
Angel Garcia Gomez146b96d2015-10-22 09:48:23 +000021static const char PointerType[] = "pointerType";
22static const char ConstructorCall[] = "constructorCall";
23static const char NewExpression[] = "newExpression";
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000024
25void MakeUniqueCheck::registerMatchers(MatchFinder *Finder) {
26 if (getLangOpts().CPlusPlus11) {
27 Finder->addMatcher(
28 cxxBindTemporaryExpr(has(
29 cxxConstructExpr(
30 hasType(qualType(hasDeclaration(classTemplateSpecializationDecl(
31 matchesName("::std::unique_ptr"),
Angel Garcia Gomezb9f30592015-10-05 12:20:17 +000032 templateArgumentCountIs(2),
33 hasTemplateArgument(0, templateArgument(refersToType(
34 qualType().bind(PointerType)))),
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000035 hasTemplateArgument(
Angel Garcia Gomezb9f30592015-10-05 12:20:17 +000036 1, templateArgument(refersToType(qualType(
37 hasDeclaration(classTemplateSpecializationDecl(
38 matchesName("::std::default_delete"),
39 templateArgumentCountIs(1),
40 hasTemplateArgument(
41 0, templateArgument(refersToType(
42 qualType(equalsBoundNode(
43 PointerType))))))))))))))),
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000044 argumentCountIs(1),
45 hasArgument(0, cxxNewExpr(hasType(pointsTo(qualType(
46 equalsBoundNode(PointerType)))))
47 .bind(NewExpression)))
48 .bind(ConstructorCall))),
49 this);
50 }
51}
52
53void MakeUniqueCheck::check(const MatchFinder::MatchResult &Result) {
54 SourceManager &SM = *Result.SourceManager;
55 const auto *Construct =
56 Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000057 const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
Angel Garcia Gomezbaf573e2015-10-14 09:22:32 +000058 const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
59
60 if (New->getNumPlacementArgs() != 0)
61 return;
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000062
63 SourceLocation ConstructCallStart = Construct->getExprLoc();
64
65 bool Invalid = false;
66 StringRef ExprStr = Lexer::getSourceText(
67 CharSourceRange::getCharRange(
68 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
69 SM, LangOptions(), &Invalid);
70 if (Invalid)
71 return;
72
73 auto Diag = diag(ConstructCallStart, "use std::make_unique instead");
74
75 // Find the location of the template's left angle.
76 size_t LAngle = ExprStr.find("<");
77 SourceLocation ConstructCallEnd;
78 if (LAngle == StringRef::npos) {
79 // If the template argument is missing (because it is part of the alias)
80 // we have to add it back.
81 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
Angel Garcia Gomez37ab7fd2015-10-22 13:20:49 +000082 Diag << FixItHint::CreateInsertion(
83 ConstructCallEnd, "<" + Type->getAsString(getLangOpts()) + ">");
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000084 } else {
85 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
86 }
87
88 Diag << FixItHint::CreateReplacement(
89 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
90 "std::make_unique");
91
Angel Garcia Gomezbaf573e2015-10-14 09:22:32 +000092 // If the unique_ptr is built with brace enclosed direct initialization, use
93 // parenthesis instead.
94 if (Construct->isListInitialization()) {
95 SourceRange BraceRange = Construct->getParenOrBraceRange();
96 Diag << FixItHint::CreateReplacement(
97 CharSourceRange::getCharRange(
98 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
99 "(");
100 Diag << FixItHint::CreateReplacement(
101 CharSourceRange::getCharRange(BraceRange.getEnd(),
102 BraceRange.getEnd().getLocWithOffset(1)),
103 ")");
104 }
105
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +0000106 SourceLocation NewStart = New->getSourceRange().getBegin();
107 SourceLocation NewEnd = New->getSourceRange().getEnd();
108 switch (New->getInitializationStyle()) {
109 case CXXNewExpr::NoInit: {
110 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
111 break;
112 }
113 case CXXNewExpr::CallInit: {
114 SourceRange InitRange = New->getDirectInitRange();
115 Diag << FixItHint::CreateRemoval(
116 SourceRange(NewStart, InitRange.getBegin()));
117 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
118 break;
119 }
120 case CXXNewExpr::ListInit: {
Angel Garcia Gomezbaf573e2015-10-14 09:22:32 +0000121 // Range of the substring that we do not want to remove.
122 SourceRange InitRange;
123 if (const auto *NewConstruct = New->getConstructExpr()) {
124 // Direct initialization with initialization list.
125 // struct S { S(int x) {} };
126 // std::unique_ptr<S>(new S{5});
127 // The arguments in the initialization list are going to be forwarded to
128 // the constructor, so this has to be replaced with:
129 // struct S { S(int x) {} };
130 // std::make_unique<S>(5);
131 InitRange = SourceRange(
132 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
133 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
134 } else {
135 // Aggregate initialization.
136 // std::unique_ptr<Pair>(new Pair{first, second});
137 // Has to be replaced with:
138 // std::make_unique<Pair>(Pair{first, second});
139 InitRange = SourceRange(
140 New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
141 New->getInitializer()->getSourceRange().getEnd());
142 }
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +0000143 Diag << FixItHint::CreateRemoval(
Angel Garcia Gomezbaf573e2015-10-14 09:22:32 +0000144 CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +0000145 Diag << FixItHint::CreateRemoval(
146 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
147 break;
148 }
149 }
150}
151
152} // namespace modernize
153} // namespace tidy
154} // namespace clang