10 #include "../utils/OptionsUtils.h"
22 AST_MATCHER_P(NamedDecl, hasAnyNameIgnoringTemplates, std::vector<StringRef>,
24 const std::string FullName =
"::" + Node.getQualifiedNameAsString();
29 std::string FullNameTrimmed;
36 }
else if (Depth == 0) {
44 const StringRef FullNameTrimmedRef = FullNameTrimmed;
45 for (
const StringRef Pattern : Names) {
46 if (Pattern.startswith(
"::")) {
47 if (FullNameTrimmed == Pattern)
49 }
else if (FullNameTrimmedRef.endswith(Pattern) &&
50 FullNameTrimmedRef.drop_back(Pattern.size()).endswith(
"::")) {
60 clang::ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
61 if (Node.getNumArgs() == 0)
64 return InnerMatcher.matches(*Node.getArg(Node.getNumArgs() - 1), Finder,
71 AST_MATCHER(CXXMemberCallExpr, hasSameNumArgsAsDeclNumParams) {
72 if (Node.getMethodDecl()->isFunctionTemplateSpecialization())
73 return Node.getNumArgs() == Node.getMethodDecl()
74 ->getPrimaryTemplate()
78 return Node.getNumArgs() == Node.getMethodDecl()->getNumParams();
82 return Node.hasExplicitTemplateArgs();
85 const auto DefaultContainersWithPushBack =
86 "::std::vector; ::std::list; ::std::deque";
87 const auto DefaultSmartPointers =
88 "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
89 const auto DefaultTupleTypes =
"::std::pair; ::std::tuple";
90 const auto DefaultTupleMakeFunctions =
"::std::make_pair; ::std::make_tuple";
91 const auto DefaultEmplacyFunctions =
92 "vector::emplace_back; vector::emplace;"
93 "deque::emplace; deque::emplace_front; deque::emplace_back;"
94 "forward_list::emplace_after; forward_list::emplace_front;"
95 "list::emplace; list::emplace_back; list::emplace_front;"
96 "set::emplace; set::emplace_hint;"
97 "map::emplace; map::emplace_hint;"
98 "multiset::emplace; multiset::emplace_hint;"
99 "multimap::emplace; multimap::emplace_hint;"
100 "unordered_set::emplace; unordered_set::emplace_hint;"
101 "unordered_map::emplace; unordered_map::emplace_hint;"
102 "unordered_multiset::emplace; unordered_multiset::emplace_hint;"
103 "unordered_multimap::emplace; unordered_multimap::emplace_hint;"
104 "stack::emplace; queue::emplace; priority_queue::emplace";
109 "IgnoreImplicitConstructors", false)),
111 "ContainersWithPushBack", DefaultContainersWithPushBack))),
113 Options.get(
"SmartPointers", DefaultSmartPointers))),
115 Options.get(
"TupleTypes", DefaultTupleTypes))),
117 Options.get(
"TupleMakeFunctions", DefaultTupleMakeFunctions))),
119 Options.get(
"EmplacyFunctions", DefaultEmplacyFunctions))) {}
130 auto CallPushBack = cxxMemberCallExpr(
131 hasDeclaration(functionDecl(hasName(
"push_back"))),
132 on(hasType(cxxRecordDecl(hasAnyName(ContainersWithPushBack)))));
134 auto CallEmplacy = cxxMemberCallExpr(
136 functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))),
137 on(hasType(cxxRecordDecl(has(typedefNameDecl(
138 hasName(
"value_type"), hasType(type(hasUnqualifiedDesugaredType(
139 recordType().bind(
"value_type"))))))))));
145 auto IsCtorOfSmartPtr =
146 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(SmartPointers))));
149 auto BitFieldAsArgument = hasAnyArgument(
150 ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
153 auto InitializerListAsArgument = hasAnyArgument(
154 ignoringImplicit(cxxConstructExpr(isListInitialization())));
157 auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
159 auto ConstructingDerived =
160 hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
163 auto IsPrivateOrProtectedCtor =
164 hasDeclaration(cxxConstructorDecl(anyOf(isPrivate(), isProtected())));
166 auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())),
167 has(cxxStdInitializerListExpr()));
171 auto SoughtConstructExpr =
173 unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
174 InitializerListAsArgument, NewExprAsArgument,
175 ConstructingDerived, IsPrivateOrProtectedCtor)))
177 auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
179 auto MakeTuple = ignoringImplicit(
180 callExpr(callee(expr(ignoringImplicit(declRefExpr(
181 unless(hasExplicitTemplateArgs()),
182 to(functionDecl(hasAnyName(TupleMakeFunctions))))))))
187 auto MakeTupleCtor = ignoringImplicit(cxxConstructExpr(
188 has(materializeTemporaryExpr(MakeTuple)),
189 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(TupleTypes))))));
191 auto SoughtParam = materializeTemporaryExpr(
192 anyOf(has(MakeTuple), has(MakeTupleCtor), HasConstructExpr,
193 has(cxxFunctionalCastExpr(HasConstructExpr))));
195 auto HasConstructExprWithValueTypeType =
196 has(ignoringImplicit(cxxConstructExpr(
197 SoughtConstructExpr, hasType(type(hasUnqualifiedDesugaredType(
198 type(equalsBoundNode(
"value_type"))))))));
200 auto HasConstructExprWithValueTypeTypeAsLastArgument =
201 hasLastArgument(materializeTemporaryExpr(anyOf(
202 HasConstructExprWithValueTypeType,
203 has(cxxFunctionalCastExpr(HasConstructExprWithValueTypeType)))));
206 traverse(TK_AsIs, cxxMemberCallExpr(CallPushBack, has(SoughtParam),
207 unless(isInTemplateInstantiation()))
208 .bind(
"push_back_call")),
214 CallEmplacy, HasConstructExprWithValueTypeTypeAsLastArgument,
215 hasSameNumArgsAsDeclNumParams(),
216 unless(isInTemplateInstantiation()))
217 .bind(
"emplacy_call")),
225 on(hasType(cxxRecordDecl(has(typedefNameDecl(
226 hasName(
"value_type"),
228 hasUnqualifiedDesugaredType(recordType(hasDeclaration(
229 cxxRecordDecl(hasAnyName(SmallVector<StringRef, 2>(
230 TupleTypes.begin(), TupleTypes.end()))))))))))))),
231 has(MakeTuple), hasSameNumArgsAsDeclNumParams(),
232 unless(isInTemplateInstantiation()))
233 .bind(
"emplacy_call")),
238 const auto *PushBackCall =
239 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"push_back_call");
240 const auto *EmplacyCall =
241 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"emplacy_call");
242 const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>(
"ctor");
243 const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>(
"make");
245 assert((PushBackCall || EmplacyCall) &&
"No call matched");
246 assert((CtorCall || MakeCall) &&
"No push_back parameter matched");
248 const CXXMemberCallExpr *Call = PushBackCall ? PushBackCall : EmplacyCall;
250 if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs() >= 1 &&
251 CtorCall->getArg(0)->getSourceRange() == CtorCall->getSourceRange())
254 const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
255 Call->getExprLoc(), Call->getArg(0)->getExprLoc());
259 ?
diag(Call->getExprLoc(),
"use emplace_back instead of push_back")
260 :
diag(CtorCall ? CtorCall->getBeginLoc() : MakeCall->getBeginLoc(),
261 "unnecessary temporary object created while calling " +
262 Call->getMethodDecl()->getName().str());
264 if (FunctionNameSourceRange.getBegin().isMacroID())
268 const char *EmplacePrefix = MakeCall ?
"emplace_back" :
"emplace_back(";
269 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
273 const SourceRange CallParensRange =
274 MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(),
275 MakeCall->getRParenLoc())
276 : CtorCall->getParenOrBraceRange();
279 if (CallParensRange.getBegin().isInvalid())
282 const SourceLocation ExprBegin =
283 CtorCall ? CtorCall->getExprLoc() : MakeCall->getExprLoc();
286 const auto ParamCallSourceRange =
287 CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
289 Diag << FixItHint::CreateRemoval(ParamCallSourceRange)
290 << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
291 CallParensRange.getEnd(), CallParensRange.getEnd()));
293 if (MakeCall && EmplacyCall) {
295 Diag << FixItHint::CreateRemoval(
296 CharSourceRange::getCharRange(MakeCall->getCallee()->getEndLoc(),
297 MakeCall->getArg(0)->getBeginLoc()));
302 Options.
store(Opts,
"IgnoreImplicitConstructors", IgnoreImplicitConstructors);