blob: a585fe3e81318327e147164752c8ed52469aca43 [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),
Angel Garcia Gomez3d7d9222015-11-04 10:27:51 +000045 hasArgument(
46 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
47 equalsBoundNode(PointerType))))))
48 .bind(NewExpression)))
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000049 .bind(ConstructorCall))),
50 this);
51 }
52}
53
54void MakeUniqueCheck::check(const MatchFinder::MatchResult &Result) {
55 SourceManager &SM = *Result.SourceManager;
56 const auto *Construct =
57 Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000058 const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
Angel Garcia Gomezbaf573e2015-10-14 09:22:32 +000059 const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
60
61 if (New->getNumPlacementArgs() != 0)
62 return;
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000063
64 SourceLocation ConstructCallStart = Construct->getExprLoc();
65
66 bool Invalid = false;
67 StringRef ExprStr = Lexer::getSourceText(
68 CharSourceRange::getCharRange(
69 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
70 SM, LangOptions(), &Invalid);
71 if (Invalid)
72 return;
73
74 auto Diag = diag(ConstructCallStart, "use std::make_unique instead");
75
76 // Find the location of the template's left angle.
77 size_t LAngle = ExprStr.find("<");
78 SourceLocation ConstructCallEnd;
79 if (LAngle == StringRef::npos) {
80 // If the template argument is missing (because it is part of the alias)
81 // we have to add it back.
82 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
Angel Garcia Gomez37ab7fd2015-10-22 13:20:49 +000083 Diag << FixItHint::CreateInsertion(
84 ConstructCallEnd, "<" + Type->getAsString(getLangOpts()) + ">");
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +000085 } else {
86 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
87 }
88
89 Diag << FixItHint::CreateReplacement(
90 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
91 "std::make_unique");
92
Angel Garcia Gomezbaf573e2015-10-14 09:22:32 +000093 // If the unique_ptr is built with brace enclosed direct initialization, use
94 // parenthesis instead.
95 if (Construct->isListInitialization()) {
96 SourceRange BraceRange = Construct->getParenOrBraceRange();
97 Diag << FixItHint::CreateReplacement(
98 CharSourceRange::getCharRange(
99 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
100 "(");
101 Diag << FixItHint::CreateReplacement(
102 CharSourceRange::getCharRange(BraceRange.getEnd(),
103 BraceRange.getEnd().getLocWithOffset(1)),
104 ")");
105 }
106
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +0000107 SourceLocation NewStart = New->getSourceRange().getBegin();
108 SourceLocation NewEnd = New->getSourceRange().getEnd();
109 switch (New->getInitializationStyle()) {
110 case CXXNewExpr::NoInit: {
111 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
112 break;
113 }
114 case CXXNewExpr::CallInit: {
115 SourceRange InitRange = New->getDirectInitRange();
116 Diag << FixItHint::CreateRemoval(
117 SourceRange(NewStart, InitRange.getBegin()));
118 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
119 break;
120 }
121 case CXXNewExpr::ListInit: {
Angel Garcia Gomezbaf573e2015-10-14 09:22:32 +0000122 // Range of the substring that we do not want to remove.
123 SourceRange InitRange;
124 if (const auto *NewConstruct = New->getConstructExpr()) {
125 // Direct initialization with initialization list.
126 // struct S { S(int x) {} };
127 // std::unique_ptr<S>(new S{5});
128 // The arguments in the initialization list are going to be forwarded to
129 // the constructor, so this has to be replaced with:
130 // struct S { S(int x) {} };
131 // std::make_unique<S>(5);
132 InitRange = SourceRange(
133 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
134 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
135 } else {
136 // Aggregate initialization.
137 // std::unique_ptr<Pair>(new Pair{first, second});
138 // Has to be replaced with:
139 // std::make_unique<Pair>(Pair{first, second});
140 InitRange = SourceRange(
141 New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
142 New->getInitializer()->getSourceRange().getEnd());
143 }
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +0000144 Diag << FixItHint::CreateRemoval(
Angel Garcia Gomezbaf573e2015-10-14 09:22:32 +0000145 CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
Angel Garcia Gomez26fd0e82015-09-29 09:36:41 +0000146 Diag << FixItHint::CreateRemoval(
147 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
148 break;
149 }
150 }
151}
152
153} // namespace modernize
154} // namespace tidy
155} // namespace clang