[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / clang-tools-extra / clang-tidy / modernize / UseEmplaceCheck.cpp
blobb85dde5644d313fcb6d4e7c3c97610a9fd2491b2
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 //===----------------------------------------------------------------------===//
9 #include "UseEmplaceCheck.h"
10 #include "../utils/OptionsUtils.h"
11 using namespace clang::ast_matchers;
13 namespace clang::tidy::modernize {
15 namespace {
16 // Identical to hasAnyName, except it does not take template specifiers into
17 // account. This is used to match the functions names as in
18 // DefaultEmplacyFunctions below without caring about the template types of the
19 // containers.
20 AST_MATCHER_P(NamedDecl, hasAnyNameIgnoringTemplates, std::vector<StringRef>,
21 Names) {
22 const std::string FullName = "::" + Node.getQualifiedNameAsString();
24 // This loop removes template specifiers by only keeping characters not within
25 // template brackets. We keep a depth count to handle nested templates. For
26 // example, it'll transform a::b<c<d>>::e<f> to simply a::b::e.
27 std::string FullNameTrimmed;
28 int Depth = 0;
29 for (const auto &Character : FullName) {
30 if (Character == '<') {
31 ++Depth;
32 } else if (Character == '>') {
33 --Depth;
34 } else if (Depth == 0) {
35 FullNameTrimmed.append(1, Character);
39 // This loop is taken from HasNameMatcher::matchesNodeFullSlow in
40 // clang/lib/ASTMatchers/ASTMatchersInternal.cpp and checks whether
41 // FullNameTrimmed matches any of the given Names.
42 const StringRef FullNameTrimmedRef = FullNameTrimmed;
43 for (const StringRef Pattern : Names) {
44 if (Pattern.startswith("::")) {
45 if (FullNameTrimmed == Pattern)
46 return true;
47 } else if (FullNameTrimmedRef.endswith(Pattern) &&
48 FullNameTrimmedRef.drop_back(Pattern.size()).endswith("::")) {
49 return true;
53 return false;
56 // Checks if the given matcher is the last argument of the given CallExpr.
57 AST_MATCHER_P(CallExpr, hasLastArgument,
58 clang::ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
59 if (Node.getNumArgs() == 0)
60 return false;
62 return InnerMatcher.matches(*Node.getArg(Node.getNumArgs() - 1), Finder,
63 Builder);
66 // Checks if the given member call has the same number of arguments as the
67 // function had parameters defined (this is useful to check if there is only one
68 // variadic argument).
69 AST_MATCHER(CXXMemberCallExpr, hasSameNumArgsAsDeclNumParams) {
70 if (const FunctionTemplateDecl *Primary =
71 Node.getMethodDecl()->getPrimaryTemplate())
72 return Node.getNumArgs() == Primary->getTemplatedDecl()->getNumParams();
74 return Node.getNumArgs() == Node.getMethodDecl()->getNumParams();
77 AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
78 return Node.hasExplicitTemplateArgs();
81 // Helper Matcher which applies the given QualType Matcher either directly or by
82 // resolving a pointer type to its pointee. Used to match v.push_back() as well
83 // as p->push_back().
84 auto hasTypeOrPointeeType(
85 const ast_matchers::internal::Matcher<QualType> &TypeMatcher) {
86 return anyOf(hasType(TypeMatcher),
87 hasType(pointerType(pointee(TypeMatcher))));
90 // Matches if the node has canonical type matching any of the given names.
91 auto hasWantedType(llvm::ArrayRef<StringRef> TypeNames) {
92 return hasCanonicalType(hasDeclaration(cxxRecordDecl(hasAnyName(TypeNames))));
95 // Matches member call expressions of the named method on the listed container
96 // types.
97 auto cxxMemberCallExprOnContainer(
98 StringRef MethodName, llvm::ArrayRef<StringRef> ContainerNames) {
99 return cxxMemberCallExpr(
100 hasDeclaration(functionDecl(hasName(MethodName))),
101 on(hasTypeOrPointeeType(hasWantedType(ContainerNames))));
104 const auto DefaultContainersWithPushBack =
105 "::std::vector; ::std::list; ::std::deque";
106 const auto DefaultContainersWithPush =
107 "::std::stack; ::std::queue; ::std::priority_queue";
108 const auto DefaultContainersWithPushFront =
109 "::std::forward_list; ::std::list; ::std::deque";
110 const auto DefaultSmartPointers =
111 "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
112 const auto DefaultTupleTypes = "::std::pair; ::std::tuple";
113 const auto DefaultTupleMakeFunctions = "::std::make_pair; ::std::make_tuple";
114 const auto DefaultEmplacyFunctions =
115 "vector::emplace_back; vector::emplace;"
116 "deque::emplace; deque::emplace_front; deque::emplace_back;"
117 "forward_list::emplace_after; forward_list::emplace_front;"
118 "list::emplace; list::emplace_back; list::emplace_front;"
119 "set::emplace; set::emplace_hint;"
120 "map::emplace; map::emplace_hint;"
121 "multiset::emplace; multiset::emplace_hint;"
122 "multimap::emplace; multimap::emplace_hint;"
123 "unordered_set::emplace; unordered_set::emplace_hint;"
124 "unordered_map::emplace; unordered_map::emplace_hint;"
125 "unordered_multiset::emplace; unordered_multiset::emplace_hint;"
126 "unordered_multimap::emplace; unordered_multimap::emplace_hint;"
127 "stack::emplace; queue::emplace; priority_queue::emplace";
128 } // namespace
130 UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
131 : ClangTidyCheck(Name, Context), IgnoreImplicitConstructors(Options.get(
132 "IgnoreImplicitConstructors", false)),
133 ContainersWithPushBack(utils::options::parseStringList(Options.get(
134 "ContainersWithPushBack", DefaultContainersWithPushBack))),
135 ContainersWithPush(utils::options::parseStringList(
136 Options.get("ContainersWithPush", DefaultContainersWithPush))),
137 ContainersWithPushFront(utils::options::parseStringList(Options.get(
138 "ContainersWithPushFront", DefaultContainersWithPushFront))),
139 SmartPointers(utils::options::parseStringList(
140 Options.get("SmartPointers", DefaultSmartPointers))),
141 TupleTypes(utils::options::parseStringList(
142 Options.get("TupleTypes", DefaultTupleTypes))),
143 TupleMakeFunctions(utils::options::parseStringList(
144 Options.get("TupleMakeFunctions", DefaultTupleMakeFunctions))),
145 EmplacyFunctions(utils::options::parseStringList(
146 Options.get("EmplacyFunctions", DefaultEmplacyFunctions))) {}
148 void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
149 // FIXME: Bunch of functionality that could be easily added:
150 // + add handling of `insert` for stl associative container, but be careful
151 // because this requires special treatment (it could cause performance
152 // regression)
153 // + match for emplace calls that should be replaced with insertion
154 auto CallPushBack =
155 cxxMemberCallExprOnContainer("push_back", ContainersWithPushBack);
156 auto CallPush = cxxMemberCallExprOnContainer("push", ContainersWithPush);
157 auto CallPushFront =
158 cxxMemberCallExprOnContainer("push_front", ContainersWithPushFront);
160 auto CallEmplacy = cxxMemberCallExpr(
161 hasDeclaration(
162 functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))),
163 on(hasTypeOrPointeeType(hasCanonicalType(hasDeclaration(
164 has(typedefNameDecl(hasName("value_type"),
165 hasType(type(hasUnqualifiedDesugaredType(
166 recordType().bind("value_type")))))))))));
168 // We can't replace push_backs of smart pointer because
169 // if emplacement fails (f.e. bad_alloc in vector) we will have leak of
170 // passed pointer because smart pointer won't be constructed
171 // (and destructed) as in push_back case.
172 auto IsCtorOfSmartPtr =
173 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(SmartPointers))));
175 // Bitfields binds only to consts and emplace_back take it by universal ref.
176 auto BitFieldAsArgument = hasAnyArgument(
177 ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
179 // Initializer list can't be passed to universal reference.
180 auto InitializerListAsArgument = hasAnyArgument(
181 ignoringImplicit(allOf(cxxConstructExpr(isListInitialization()),
182 unless(cxxTemporaryObjectExpr()))));
184 // We could have leak of resource.
185 auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
186 // We would call another constructor.
187 auto ConstructingDerived =
188 hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
190 // emplace_back can't access private or protected constructors.
191 auto IsPrivateOrProtectedCtor =
192 hasDeclaration(cxxConstructorDecl(anyOf(isPrivate(), isProtected())));
194 auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())),
195 has(cxxStdInitializerListExpr()));
197 // FIXME: Discard 0/NULL (as nullptr), static inline const data members,
198 // overloaded functions and template names.
199 auto SoughtConstructExpr =
200 cxxConstructExpr(
201 unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
202 InitializerListAsArgument, NewExprAsArgument,
203 ConstructingDerived, IsPrivateOrProtectedCtor)))
204 .bind("ctor");
205 auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
207 // allow for T{} to be replaced, even if no CTOR is declared
208 auto HasConstructInitListExpr = has(initListExpr(anyOf(
209 allOf(has(SoughtConstructExpr),
210 has(cxxConstructExpr(argumentCountIs(0)))),
211 has(cxxBindTemporaryExpr(has(SoughtConstructExpr),
212 has(cxxConstructExpr(argumentCountIs(0))))))));
213 auto HasBracedInitListExpr =
214 anyOf(has(cxxBindTemporaryExpr(HasConstructInitListExpr)),
215 HasConstructInitListExpr);
217 auto MakeTuple = ignoringImplicit(
218 callExpr(callee(expr(ignoringImplicit(declRefExpr(
219 unless(hasExplicitTemplateArgs()),
220 to(functionDecl(hasAnyName(TupleMakeFunctions))))))))
221 .bind("make"));
223 // make_something can return type convertible to container's element type.
224 // Allow the conversion only on containers of pairs.
225 auto MakeTupleCtor = ignoringImplicit(cxxConstructExpr(
226 has(materializeTemporaryExpr(MakeTuple)),
227 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(TupleTypes))))));
229 auto SoughtParam =
230 materializeTemporaryExpr(
231 anyOf(has(MakeTuple), has(MakeTupleCtor), HasConstructExpr,
232 HasBracedInitListExpr,
233 has(cxxFunctionalCastExpr(HasConstructExpr)),
234 has(cxxFunctionalCastExpr(HasBracedInitListExpr))))
235 .bind("temporary_expr");
237 auto HasConstructExprWithValueTypeType =
238 has(ignoringImplicit(cxxConstructExpr(
239 SoughtConstructExpr, hasType(type(hasUnqualifiedDesugaredType(
240 type(equalsBoundNode("value_type"))))))));
242 auto HasBracedInitListWithValueTypeType =
243 anyOf(allOf(HasConstructInitListExpr,
244 has(initListExpr(hasType(type(hasUnqualifiedDesugaredType(
245 type(equalsBoundNode("value_type")))))))),
246 has(cxxBindTemporaryExpr(
247 HasConstructInitListExpr,
248 has(initListExpr(hasType(type(hasUnqualifiedDesugaredType(
249 type(equalsBoundNode("value_type"))))))))));
251 auto HasConstructExprWithValueTypeTypeAsLastArgument = hasLastArgument(
252 materializeTemporaryExpr(
253 anyOf(HasConstructExprWithValueTypeType,
254 HasBracedInitListWithValueTypeType,
255 has(cxxFunctionalCastExpr(HasConstructExprWithValueTypeType)),
256 has(cxxFunctionalCastExpr(HasBracedInitListWithValueTypeType))))
257 .bind("temporary_expr"));
259 Finder->addMatcher(
260 traverse(TK_AsIs, cxxMemberCallExpr(CallPushBack, has(SoughtParam),
261 unless(isInTemplateInstantiation()))
262 .bind("push_back_call")),
263 this);
265 Finder->addMatcher(
266 traverse(TK_AsIs, cxxMemberCallExpr(CallPush, has(SoughtParam),
267 unless(isInTemplateInstantiation()))
268 .bind("push_call")),
269 this);
271 Finder->addMatcher(
272 traverse(TK_AsIs, cxxMemberCallExpr(CallPushFront, has(SoughtParam),
273 unless(isInTemplateInstantiation()))
274 .bind("push_front_call")),
275 this);
277 Finder->addMatcher(
278 traverse(TK_AsIs,
279 cxxMemberCallExpr(
280 CallEmplacy, HasConstructExprWithValueTypeTypeAsLastArgument,
281 hasSameNumArgsAsDeclNumParams(),
282 unless(isInTemplateInstantiation()))
283 .bind("emplacy_call")),
284 this);
286 Finder->addMatcher(
287 traverse(
288 TK_AsIs,
289 cxxMemberCallExpr(
290 CallEmplacy,
291 on(hasType(cxxRecordDecl(has(typedefNameDecl(
292 hasName("value_type"),
293 hasType(type(
294 hasUnqualifiedDesugaredType(recordType(hasDeclaration(
295 cxxRecordDecl(hasAnyName(SmallVector<StringRef, 2>(
296 TupleTypes.begin(), TupleTypes.end()))))))))))))),
297 has(MakeTuple), hasSameNumArgsAsDeclNumParams(),
298 unless(isInTemplateInstantiation()))
299 .bind("emplacy_call")),
300 this);
303 void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
304 const auto *PushBackCall =
305 Result.Nodes.getNodeAs<CXXMemberCallExpr>("push_back_call");
306 const auto *PushCall = Result.Nodes.getNodeAs<CXXMemberCallExpr>("push_call");
307 const auto *PushFrontCall =
308 Result.Nodes.getNodeAs<CXXMemberCallExpr>("push_front_call");
309 const auto *EmplacyCall =
310 Result.Nodes.getNodeAs<CXXMemberCallExpr>("emplacy_call");
311 const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
312 const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>("make");
313 const auto *TemporaryExpr =
314 Result.Nodes.getNodeAs<MaterializeTemporaryExpr>("temporary_expr");
316 const CXXMemberCallExpr *Call = [&]() {
317 if (PushBackCall) {
318 return PushBackCall;
320 if (PushCall) {
321 return PushCall;
323 if (PushFrontCall) {
324 return PushFrontCall;
326 return EmplacyCall;
327 }();
329 assert(Call && "No call matched");
330 assert((CtorCall || MakeCall) && "No push_back parameter matched");
332 if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs() >= 1 &&
333 CtorCall->getArg(0)->getSourceRange() == CtorCall->getSourceRange())
334 return;
336 const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
337 Call->getExprLoc(), Call->getArg(0)->getExprLoc());
339 auto Diag =
340 EmplacyCall
341 ? diag(TemporaryExpr ? TemporaryExpr->getBeginLoc()
342 : CtorCall ? CtorCall->getBeginLoc()
343 : MakeCall->getBeginLoc(),
344 "unnecessary temporary object created while calling %0")
345 : diag(Call->getExprLoc(), "use emplace%select{|_back|_front}0 "
346 "instead of push%select{|_back|_front}0");
347 if (EmplacyCall)
348 Diag << Call->getMethodDecl()->getName();
349 else if (PushCall)
350 Diag << 0;
351 else if (PushBackCall)
352 Diag << 1;
353 else
354 Diag << 2;
356 if (FunctionNameSourceRange.getBegin().isMacroID())
357 return;
359 if (PushBackCall) {
360 const char *EmplacePrefix = MakeCall ? "emplace_back" : "emplace_back(";
361 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
362 EmplacePrefix);
363 } else if (PushCall) {
364 const char *EmplacePrefix = MakeCall ? "emplace" : "emplace(";
365 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
366 EmplacePrefix);
367 } else if (PushFrontCall) {
368 const char *EmplacePrefix = MakeCall ? "emplace_front" : "emplace_front(";
369 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
370 EmplacePrefix);
373 const SourceRange CallParensRange =
374 MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(),
375 MakeCall->getRParenLoc())
376 : CtorCall->getParenOrBraceRange();
378 // Finish if there is no explicit constructor call.
379 if (CallParensRange.getBegin().isInvalid())
380 return;
382 // FIXME: Will there ever be a CtorCall, if there is no TemporaryExpr?
383 const SourceLocation ExprBegin = TemporaryExpr ? TemporaryExpr->getExprLoc()
384 : CtorCall ? CtorCall->getExprLoc()
385 : MakeCall->getExprLoc();
387 // Range for constructor name and opening brace.
388 const auto ParamCallSourceRange =
389 CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
391 // Range for constructor closing brace and end of temporary expr.
392 const auto EndCallSourceRange = CharSourceRange::getTokenRange(
393 CallParensRange.getEnd(),
394 TemporaryExpr ? TemporaryExpr->getEndLoc() : CallParensRange.getEnd());
396 Diag << FixItHint::CreateRemoval(ParamCallSourceRange)
397 << FixItHint::CreateRemoval(EndCallSourceRange);
399 if (MakeCall && EmplacyCall) {
400 // Remove extra left parenthesis
401 Diag << FixItHint::CreateRemoval(
402 CharSourceRange::getCharRange(MakeCall->getCallee()->getEndLoc(),
403 MakeCall->getArg(0)->getBeginLoc()));
407 void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
408 Options.store(Opts, "IgnoreImplicitConstructors", IgnoreImplicitConstructors);
409 Options.store(Opts, "ContainersWithPushBack",
410 utils::options::serializeStringList(ContainersWithPushBack));
411 Options.store(Opts, "ContainersWithPush",
412 utils::options::serializeStringList(ContainersWithPush));
413 Options.store(Opts, "ContainersWithPushFront",
414 utils::options::serializeStringList(ContainersWithPushFront));
415 Options.store(Opts, "SmartPointers",
416 utils::options::serializeStringList(SmartPointers));
417 Options.store(Opts, "TupleTypes",
418 utils::options::serializeStringList(TupleTypes));
419 Options.store(Opts, "TupleMakeFunctions",
420 utils::options::serializeStringList(TupleMakeFunctions));
421 Options.store(Opts, "EmplacyFunctions",
422 utils::options::serializeStringList(EmplacyFunctions));
425 } // namespace clang::tidy::modernize