clang-tools  3.8.0
MakeUniqueCheck.cpp
Go to the documentation of this file.
1 //===--- 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 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace modernize {
20 
21 static const char PointerType[] = "pointerType";
22 static const char ConstructorCall[] = "constructorCall";
23 static const char NewExpression[] = "newExpression";
24 
25 void 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"),
32  templateArgumentCountIs(2),
33  hasTemplateArgument(0, templateArgument(refersToType(
34  qualType().bind(PointerType)))),
35  hasTemplateArgument(
36  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))))))))))))))),
44  argumentCountIs(1),
45  hasArgument(
46  0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
47  equalsBoundNode(PointerType))))))
48  .bind(NewExpression)))
49  .bind(ConstructorCall))),
50  this);
51  }
52 }
53 
54 void MakeUniqueCheck::check(const MatchFinder::MatchResult &Result) {
55  SourceManager &SM = *Result.SourceManager;
56  const auto *Construct =
57  Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
58  const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
59  const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
60 
61  if (New->getNumPlacementArgs() != 0)
62  return;
63 
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());
83  Diag << FixItHint::CreateInsertion(
84  ConstructCallEnd, "<" + Type->getAsString(getLangOpts()) + ">");
85  } else {
86  ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
87  }
88 
89  Diag << FixItHint::CreateReplacement(
90  CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
91  "std::make_unique");
92 
93  // 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 
107  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: {
122  // 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  }
144  Diag << FixItHint::CreateRemoval(
145  CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
146  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
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
SourceManager & SM
static const char PointerType[]
static const char ConstructorCall[]
static const char NewExpression[]
const NamedDecl * Result
Definition: USRFinder.cpp:121