clang-tools  5.0.0
UseEmplaceCheck.cpp
Go to the documentation of this file.
1 //===--- UseEmplaceCheck.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 "UseEmplaceCheck.h"
11 #include "../utils/OptionsUtils.h"
12 using namespace clang::ast_matchers;
13 
14 namespace clang {
15 namespace tidy {
16 namespace modernize {
17 
18 namespace {
19 AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
20  return Node.hasExplicitTemplateArgs();
21 }
22 
23 const auto DefaultContainersWithPushBack =
24  "::std::vector; ::std::list; ::std::deque";
25 const auto DefaultSmartPointers =
26  "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
27 const auto DefaultTupleTypes = "::std::pair; ::std::tuple";
28 const auto DefaultTupleMakeFunctions = "::std::make_pair; ::std::make_tuple";
29 } // namespace
30 
31 UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
32  : ClangTidyCheck(Name, Context),
33  ContainersWithPushBack(utils::options::parseStringList(Options.get(
34  "ContainersWithPushBack", DefaultContainersWithPushBack))),
35  SmartPointers(utils::options::parseStringList(
36  Options.get("SmartPointers", DefaultSmartPointers))),
37  TupleTypes(utils::options::parseStringList(
38  Options.get("TupleTypes", DefaultTupleTypes))),
39  TupleMakeFunctions(utils::options::parseStringList(
40  Options.get("TupleMakeFunctions", DefaultTupleMakeFunctions))) {}
41 
43  if (!getLangOpts().CPlusPlus11)
44  return;
45 
46  // FIXME: Bunch of functionality that could be easily added:
47  // + add handling of `push_front` for std::forward_list, std::list
48  // and std::deque.
49  // + add handling of `push` for std::stack, std::queue, std::priority_queue
50  // + add handling of `insert` for stl associative container, but be careful
51  // because this requires special treatment (it could cause performance
52  // regression)
53  // + match for emplace calls that should be replaced with insertion
54  auto CallPushBack = cxxMemberCallExpr(
55  hasDeclaration(functionDecl(hasName("push_back"))),
56  on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
57  ContainersWithPushBack.begin(), ContainersWithPushBack.end()))))));
58 
59  // We can't replace push_backs of smart pointer because
60  // if emplacement fails (f.e. bad_alloc in vector) we will have leak of
61  // passed pointer because smart pointer won't be constructed
62  // (and destructed) as in push_back case.
63  auto IsCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
64  SmallVector<StringRef, 5>(SmartPointers.begin(), SmartPointers.end())))));
65 
66  // Bitfields binds only to consts and emplace_back take it by universal ref.
67  auto BitFieldAsArgument = hasAnyArgument(
68  ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
69 
70  // Initializer list can't be passed to universal reference.
71  auto InitializerListAsArgument = hasAnyArgument(
72  ignoringImplicit(cxxConstructExpr(isListInitialization())));
73 
74  // We could have leak of resource.
75  auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
76  // We would call another constructor.
77  auto ConstructingDerived =
78  hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
79 
80  // emplace_back can't access private constructor.
81  auto IsPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate()));
82 
83  auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())),
84  has(cxxStdInitializerListExpr()));
85 
86  // FIXME: Discard 0/NULL (as nullptr), static inline const data members,
87  // overloaded functions and template names.
88  auto SoughtConstructExpr =
89  cxxConstructExpr(
90  unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
91  InitializerListAsArgument, NewExprAsArgument,
92  ConstructingDerived, IsPrivateCtor)))
93  .bind("ctor");
94  auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
95 
96  auto MakeTuple = ignoringImplicit(
97  callExpr(
98  callee(expr(ignoringImplicit(declRefExpr(
99  unless(hasExplicitTemplateArgs()),
100  to(functionDecl(hasAnyName(SmallVector<StringRef, 2>(
101  TupleMakeFunctions.begin(), TupleMakeFunctions.end())))))))))
102  .bind("make"));
103 
104  // make_something can return type convertible to container's element type.
105  // Allow the conversion only on containers of pairs.
106  auto MakeTupleCtor = ignoringImplicit(cxxConstructExpr(
107  has(materializeTemporaryExpr(MakeTuple)),
108  hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
109  SmallVector<StringRef, 2>(TupleTypes.begin(), TupleTypes.end())))))));
110 
111  auto SoughtParam = materializeTemporaryExpr(
112  anyOf(has(MakeTuple), has(MakeTupleCtor),
113  HasConstructExpr, has(cxxFunctionalCastExpr(HasConstructExpr))));
114 
115  Finder->addMatcher(cxxMemberCallExpr(CallPushBack, has(SoughtParam),
116  unless(isInTemplateInstantiation()))
117  .bind("call"),
118  this);
119 }
120 
121 void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
122  const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
123  const auto *InnerCtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
124  const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>("make");
125  assert((InnerCtorCall || MakeCall) && "No push_back parameter matched");
126 
127  const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
128  Call->getExprLoc(), Call->getArg(0)->getExprLoc());
129 
130  auto Diag = diag(Call->getExprLoc(), "use emplace_back instead of push_back");
131 
132  if (FunctionNameSourceRange.getBegin().isMacroID())
133  return;
134 
135  const auto *EmplacePrefix = MakeCall ? "emplace_back" : "emplace_back(";
136  Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, EmplacePrefix);
137 
138  const SourceRange CallParensRange =
139  MakeCall ? SourceRange(MakeCall->getCallee()->getLocEnd(),
140  MakeCall->getRParenLoc())
141  : InnerCtorCall->getParenOrBraceRange();
142 
143  // Finish if there is no explicit constructor call.
144  if (CallParensRange.getBegin().isInvalid())
145  return;
146 
147  const SourceLocation ExprBegin =
148  MakeCall ? MakeCall->getExprLoc() : InnerCtorCall->getExprLoc();
149 
150  // Range for constructor name and opening brace.
151  const auto ParamCallSourceRange =
152  CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
153 
154  Diag << FixItHint::CreateRemoval(ParamCallSourceRange)
155  << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
156  CallParensRange.getEnd(), CallParensRange.getEnd()));
157 }
158 
160  Options.store(Opts, "ContainersWithPushBack",
161  utils::options::serializeStringList(ContainersWithPushBack));
162  Options.store(Opts, "SmartPointers",
163  utils::options::serializeStringList(SmartPointers));
164  Options.store(Opts, "TupleTypes",
166  Options.store(Opts, "TupleMakeFunctions",
167  utils::options::serializeStringList(TupleMakeFunctions));
168 }
169 
170 } // namespace modernize
171 } // namespace tidy
172 } // namespace clang
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
StringHandle Name
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:275
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:449
std::map< std::string, std::string > OptionMap
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
AST_MATCHER(VarDecl, isAsm)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:416