clang-tools 10.0.0

UseEmplaceCheck.cpp
Go to the documentation of this file.
1 //===--- UseEmplaceCheck.cpp - clang-tidy----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "UseEmplaceCheck.h"
10 #include "../utils/OptionsUtils.h"
11 using namespace clang::ast_matchers;
12
13 namespace clang {
14 namespace tidy {
15 namespace modernize {
16
17 namespace {
18 AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
19 return Node.hasExplicitTemplateArgs();
20 }
21
22 const auto DefaultContainersWithPushBack =
23 "::std::vector; ::std::list; ::std::deque";
24 const auto DefaultSmartPointers =
25 "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
26 const auto DefaultTupleTypes = "::std::pair; ::std::tuple";
27 const auto DefaultTupleMakeFunctions = "::std::make_pair; ::std::make_tuple";
28 } // namespace
29
30 UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
31 : ClangTidyCheck(Name, Context),
32 IgnoreImplicitConstructors(Options.get("IgnoreImplicitConstructors", 0)),
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
42 void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
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 *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
124 const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>("make");
125 assert((CtorCall || MakeCall) && "No push_back parameter matched");
126
127 if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs() >= 1 &&
128 CtorCall->getArg(0)->getSourceRange() == CtorCall->getSourceRange())
129 return;
130
131 const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
132 Call->getExprLoc(), Call->getArg(0)->getExprLoc());
133
134 auto Diag = diag(Call->getExprLoc(), "use emplace_back instead of push_back");
135
136 if (FunctionNameSourceRange.getBegin().isMacroID())
137 return;
138
139 const auto *EmplacePrefix = MakeCall ? "emplace_back" : "emplace_back(";
140 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, EmplacePrefix);
141
142 const SourceRange CallParensRange =
143 MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(),
144 MakeCall->getRParenLoc())
145 : CtorCall->getParenOrBraceRange();
146
147 // Finish if there is no explicit constructor call.
148 if (CallParensRange.getBegin().isInvalid())
149 return;
150
151 const SourceLocation ExprBegin =
152 MakeCall ? MakeCall->getExprLoc() : CtorCall->getExprLoc();
153
154 // Range for constructor name and opening brace.
155 const auto ParamCallSourceRange =
156 CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
157
158 Diag << FixItHint::CreateRemoval(ParamCallSourceRange)
159 << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
160 CallParensRange.getEnd(), CallParensRange.getEnd()));
161 }
162
163 void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
164 Options.store(Opts, "ContainersWithPushBack",
165 utils::options::serializeStringList(ContainersWithPushBack));
166 Options.store(Opts, "SmartPointers",
167 utils::options::serializeStringList(SmartPointers));
168 Options.store(Opts, "TupleTypes",
169 utils::options::serializeStringList(TupleTypes));
170 Options.store(Opts, "TupleMakeFunctions",
171 utils::options::serializeStringList(TupleMakeFunctions));
172 }
173
174 } // namespace modernize
175 } // namespace tidy
176 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:14
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:30
UseEmplaceCheck.h
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:163
clang::ast_matchers::AST_MATCHER
AST_MATCHER(Expr, isMacroID)
Definition: PreferIsaOrDynCastInConditionalsCheck.cpp:18
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:188
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:121
clang::tidy::utils::options::parseStringList
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
Definition: OptionsUtils.cpp:18
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:184
clang::tidy::modernize::UseEmplaceCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: UseEmplaceCheck.cpp:42
clang::tidy::ClangTidyCheck::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: ClangTidyCheck.cpp:58
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:97
clang::clangd::getTokenRange
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
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: ClangTidyCheck.cpp:21