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