clang-tools  3.9.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 static const auto DefaultContainersWithPushBack =
19  "::std::vector; ::std::list; ::std::deque";
20 static const auto DefaultSmartPointers =
21  "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
22 
23 UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
24  : ClangTidyCheck(Name, Context),
25  ContainersWithPushBack(utils::options::parseStringList(Options.get(
26  "ContainersWithPushBack", DefaultContainersWithPushBack))),
27  SmartPointers(utils::options::parseStringList(
28  Options.get("SmartPointers", DefaultSmartPointers))) {}
29 
31  if (!getLangOpts().CPlusPlus11)
32  return;
33 
34  // FIXME: Bunch of functionality that could be easily added:
35  // + add handling of `push_front` for std::forward_list, std::list
36  // and std::deque.
37  // + add handling of `push` for std::stack, std::queue, std::priority_queue
38  // + add handling of `insert` for stl associative container, but be careful
39  // because this requires special treatment (it could cause performance
40  // regression)
41  // + match for emplace calls that should be replaced with insertion
42  // + match for make_pair calls.
43  auto callPushBack = cxxMemberCallExpr(
44  hasDeclaration(functionDecl(hasName("push_back"))),
45  on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
46  ContainersWithPushBack.begin(), ContainersWithPushBack.end()))))));
47 
48  // We can't replace push_backs of smart pointer because
49  // if emplacement fails (f.e. bad_alloc in vector) we will have leak of
50  // passed pointer because smart pointer won't be constructed
51  // (and destructed) as in push_back case.
52  auto isCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
53  SmallVector<StringRef, 5>(SmartPointers.begin(), SmartPointers.end())))));
54 
55  // Bitfields binds only to consts and emplace_back take it by universal ref.
56  auto bitFieldAsArgument = hasAnyArgument(
57  ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
58 
59  // Initializer list can't be passed to universal reference.
60  auto initializerListAsArgument = hasAnyArgument(
61  ignoringImplicit(cxxConstructExpr(isListInitialization())));
62 
63  // We could have leak of resource.
64  auto newExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
65  // We would call another constructor.
66  auto constructingDerived =
67  hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
68 
69  // emplace_back can't access private constructor.
70  auto isPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate()));
71 
72  auto hasInitList = has(ignoringImplicit(initListExpr()));
73  // FIXME: Discard 0/NULL (as nullptr), static inline const data members,
74  // overloaded functions and template names.
75  auto soughtConstructExpr =
76  cxxConstructExpr(
77  unless(anyOf(isCtorOfSmartPtr, hasInitList, bitFieldAsArgument,
78  initializerListAsArgument, newExprAsArgument,
79  constructingDerived, isPrivateCtor)))
80  .bind("ctor");
81  auto hasConstructExpr = has(ignoringImplicit(soughtConstructExpr));
82 
83  auto ctorAsArgument = materializeTemporaryExpr(
84  anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr))));
85 
86  Finder->addMatcher(cxxMemberCallExpr(callPushBack, has(ctorAsArgument),
87  unless(isInTemplateInstantiation()))
88  .bind("call"),
89  this);
90 }
91 
92 void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
93  const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
94  const auto *InnerCtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
95 
96  auto FunctionNameSourceRange = CharSourceRange::getCharRange(
97  Call->getExprLoc(), Call->getArg(0)->getExprLoc());
98 
99  auto Diag = diag(Call->getExprLoc(), "use emplace_back instead of push_back");
100 
101  if (FunctionNameSourceRange.getBegin().isMacroID())
102  return;
103 
104  Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
105  "emplace_back(");
106 
107  auto CallParensRange = InnerCtorCall->getParenOrBraceRange();
108 
109  // Finish if there is no explicit constructor call.
110  if (CallParensRange.getBegin().isInvalid())
111  return;
112 
113  // Range for constructor name and opening brace.
114  auto CtorCallSourceRange = CharSourceRange::getTokenRange(
115  InnerCtorCall->getExprLoc(), CallParensRange.getBegin());
116 
117  Diag << FixItHint::CreateRemoval(CtorCallSourceRange)
118  << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
119  CallParensRange.getEnd(), CallParensRange.getEnd()));
120 }
121 
123  Options.store(Opts, "ContainersWithPushBack",
124  utils::options::serializeStringList(ContainersWithPushBack));
125  Options.store(Opts, "SmartPointers",
126  utils::options::serializeStringList(SmartPointers));
127 }
128 
129 } // namespace modernize
130 } // namespace tidy
131 } // namespace clang
const std::string Name
Definition: USRFinder.cpp:140
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:170
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
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:210
Base class for all clang-tidy checks.
Definition: ClangTidy.h:110
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static const auto DefaultContainersWithPushBack
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:385
std::map< std::string, std::string > OptionMap
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:352
const NamedDecl * Result
Definition: USRFinder.cpp:137
static const auto DefaultSmartPointers