10#include "../utils/OptionsUtils.h"
17 return Node.getNumInits() <= N;
24AST_MATCHER_P(NamedDecl, hasAnyNameIgnoringTemplates, std::vector<StringRef>,
26 const std::string FullName =
"::" +
Node.getQualifiedNameAsString();
31 std::string FullNameTrimmed;
33 for (
const auto &Character : FullName) {
34 if (Character ==
'<') {
36 }
else if (Character ==
'>') {
38 }
else if (Depth == 0) {
39 FullNameTrimmed.append(1, Character);
46 const StringRef FullNameTrimmedRef = FullNameTrimmed;
47 for (
const StringRef Pattern : Names) {
48 if (Pattern.starts_with(
"::")) {
49 if (FullNameTrimmed == Pattern)
51 }
else if (FullNameTrimmedRef.ends_with(Pattern) &&
52 FullNameTrimmedRef.drop_back(Pattern.size()).ends_with(
"::")) {
62 clang::ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
63 if (
Node.getNumArgs() == 0)
66 return InnerMatcher.matches(*
Node.getArg(
Node.getNumArgs() - 1), Finder,
73AST_MATCHER(CXXMemberCallExpr, hasSameNumArgsAsDeclNumParams) {
74 if (
const FunctionTemplateDecl *Primary =
75 Node.getMethodDecl()->getPrimaryTemplate())
76 return Node.getNumArgs() == Primary->getTemplatedDecl()->getNumParams();
78 return Node.getNumArgs() ==
Node.getMethodDecl()->getNumParams();
82 return Node.hasExplicitTemplateArgs();
88auto hasTypeOrPointeeType(
89 const ast_matchers::internal::Matcher<QualType> &TypeMatcher) {
90 return anyOf(hasType(TypeMatcher),
91 hasType(pointerType(pointee(TypeMatcher))));
95auto hasWantedType(llvm::ArrayRef<StringRef> TypeNames) {
96 return hasCanonicalType(hasDeclaration(cxxRecordDecl(hasAnyName(TypeNames))));
101auto cxxMemberCallExprOnContainer(
102 StringRef MethodName, llvm::ArrayRef<StringRef> ContainerNames) {
103 return cxxMemberCallExpr(
104 hasDeclaration(functionDecl(hasName(MethodName))),
105 on(hasTypeOrPointeeType(hasWantedType(ContainerNames))));
108const auto DefaultContainersWithPushBack =
109 "::std::vector; ::std::list; ::std::deque";
110const auto DefaultContainersWithPush =
111 "::std::stack; ::std::queue; ::std::priority_queue";
112const auto DefaultContainersWithPushFront =
113 "::std::forward_list; ::std::list; ::std::deque";
114const auto DefaultSmartPointers =
115 "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
116const auto DefaultTupleTypes =
"::std::pair; ::std::tuple";
117const auto DefaultTupleMakeFunctions =
"::std::make_pair; ::std::make_tuple";
118const auto DefaultEmplacyFunctions =
119 "vector::emplace_back; vector::emplace;"
120 "deque::emplace; deque::emplace_front; deque::emplace_back;"
121 "forward_list::emplace_after; forward_list::emplace_front;"
122 "list::emplace; list::emplace_back; list::emplace_front;"
123 "set::emplace; set::emplace_hint;"
124 "map::emplace; map::emplace_hint;"
125 "multiset::emplace; multiset::emplace_hint;"
126 "multimap::emplace; multimap::emplace_hint;"
127 "unordered_set::emplace; unordered_set::emplace_hint;"
128 "unordered_map::emplace; unordered_map::emplace_hint;"
129 "unordered_multiset::emplace; unordered_multiset::emplace_hint;"
130 "unordered_multimap::emplace; unordered_multimap::emplace_hint;"
131 "stack::emplace; queue::emplace; priority_queue::emplace";
136 "IgnoreImplicitConstructors", false)),
137 ContainersWithPushBack(utils::options::parseStringList(Options.get(
138 "ContainersWithPushBack", DefaultContainersWithPushBack))),
139 ContainersWithPush(utils::options::parseStringList(
140 Options.get(
"ContainersWithPush", DefaultContainersWithPush))),
141 ContainersWithPushFront(utils::options::parseStringList(Options.get(
142 "ContainersWithPushFront", DefaultContainersWithPushFront))),
143 SmartPointers(utils::options::parseStringList(
144 Options.get(
"SmartPointers", DefaultSmartPointers))),
145 TupleTypes(utils::options::parseStringList(
146 Options.get(
"TupleTypes", DefaultTupleTypes))),
147 TupleMakeFunctions(utils::options::parseStringList(
148 Options.get(
"TupleMakeFunctions", DefaultTupleMakeFunctions))),
149 EmplacyFunctions(utils::options::parseStringList(
150 Options.get(
"EmplacyFunctions", DefaultEmplacyFunctions))) {}
159 cxxMemberCallExprOnContainer(
"push_back", ContainersWithPushBack);
160 auto CallPush = cxxMemberCallExprOnContainer(
"push", ContainersWithPush);
162 cxxMemberCallExprOnContainer(
"push_front", ContainersWithPushFront);
164 auto CallEmplacy = cxxMemberCallExpr(
166 functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))),
167 on(hasTypeOrPointeeType(hasCanonicalType(hasDeclaration(
168 has(typedefNameDecl(hasName(
"value_type"),
169 hasType(type(hasUnqualifiedDesugaredType(
170 recordType().bind(
"value_type")))))))))));
176 auto IsCtorOfSmartPtr =
177 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(SmartPointers))));
180 auto BitFieldAsArgument = hasAnyArgument(
181 ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
184 auto InitializerListAsArgument = hasAnyArgument(
185 ignoringImplicit(allOf(cxxConstructExpr(isListInitialization()),
186 unless(cxxTemporaryObjectExpr()))));
189 auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
191 auto ConstructingDerived =
192 hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
195 auto IsPrivateOrProtectedCtor =
196 hasDeclaration(cxxConstructorDecl(anyOf(isPrivate(), isProtected())));
198 auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())),
199 has(cxxStdInitializerListExpr()));
203 auto SoughtConstructExpr =
205 unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
206 InitializerListAsArgument, NewExprAsArgument,
207 ConstructingDerived, IsPrivateOrProtectedCtor)))
209 auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
212 auto HasConstructInitListExpr = has(initListExpr(
213 initCountLeq(1), anyOf(allOf(has(SoughtConstructExpr),
214 has(cxxConstructExpr(argumentCountIs(0)))),
215 has(cxxBindTemporaryExpr(
216 has(SoughtConstructExpr),
217 has(cxxConstructExpr(argumentCountIs(0))))))));
218 auto HasBracedInitListExpr =
219 anyOf(has(cxxBindTemporaryExpr(HasConstructInitListExpr)),
220 HasConstructInitListExpr);
222 auto MakeTuple = ignoringImplicit(
223 callExpr(callee(expr(ignoringImplicit(declRefExpr(
224 unless(hasExplicitTemplateArgs()),
225 to(functionDecl(hasAnyName(TupleMakeFunctions))))))))
230 auto MakeTupleCtor = ignoringImplicit(cxxConstructExpr(
231 has(materializeTemporaryExpr(MakeTuple)),
232 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(TupleTypes))))));
235 materializeTemporaryExpr(
236 anyOf(has(MakeTuple), has(MakeTupleCtor), HasConstructExpr,
237 HasBracedInitListExpr,
238 has(cxxFunctionalCastExpr(HasConstructExpr)),
239 has(cxxFunctionalCastExpr(HasBracedInitListExpr))))
240 .bind(
"temporary_expr");
242 auto HasConstructExprWithValueTypeType =
243 has(ignoringImplicit(cxxConstructExpr(
244 SoughtConstructExpr, hasType(type(hasUnqualifiedDesugaredType(
245 type(equalsBoundNode(
"value_type"))))))));
247 auto HasBracedInitListWithValueTypeType =
248 anyOf(allOf(HasConstructInitListExpr,
249 has(initListExpr(hasType(type(hasUnqualifiedDesugaredType(
250 type(equalsBoundNode(
"value_type")))))))),
251 has(cxxBindTemporaryExpr(
252 HasConstructInitListExpr,
253 has(initListExpr(hasType(type(hasUnqualifiedDesugaredType(
254 type(equalsBoundNode(
"value_type"))))))))));
256 auto HasConstructExprWithValueTypeTypeAsLastArgument = hasLastArgument(
257 materializeTemporaryExpr(
258 anyOf(HasConstructExprWithValueTypeType,
259 HasBracedInitListWithValueTypeType,
260 has(cxxFunctionalCastExpr(HasConstructExprWithValueTypeType)),
261 has(cxxFunctionalCastExpr(HasBracedInitListWithValueTypeType))))
262 .bind(
"temporary_expr"));
265 traverse(TK_AsIs, cxxMemberCallExpr(CallPushBack, has(SoughtParam),
266 unless(isInTemplateInstantiation()))
267 .bind(
"push_back_call")),
271 traverse(TK_AsIs, cxxMemberCallExpr(CallPush, has(SoughtParam),
272 unless(isInTemplateInstantiation()))
277 traverse(TK_AsIs, cxxMemberCallExpr(CallPushFront, has(SoughtParam),
278 unless(isInTemplateInstantiation()))
279 .bind(
"push_front_call")),
285 CallEmplacy, HasConstructExprWithValueTypeTypeAsLastArgument,
286 hasSameNumArgsAsDeclNumParams(),
287 unless(isInTemplateInstantiation()))
288 .bind(
"emplacy_call")),
296 on(hasType(cxxRecordDecl(has(typedefNameDecl(
297 hasName(
"value_type"),
299 hasUnqualifiedDesugaredType(recordType(hasDeclaration(
300 cxxRecordDecl(hasAnyName(SmallVector<StringRef, 2>(
301 TupleTypes.begin(), TupleTypes.end()))))))))))))),
302 has(MakeTuple), hasSameNumArgsAsDeclNumParams(),
303 unless(isInTemplateInstantiation()))
304 .bind(
"emplacy_call")),
309 const auto *PushBackCall =
310 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"push_back_call");
311 const auto *PushCall = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"push_call");
312 const auto *PushFrontCall =
313 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"push_front_call");
314 const auto *EmplacyCall =
315 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"emplacy_call");
316 const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>(
"ctor");
317 const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>(
"make");
318 const auto *TemporaryExpr =
319 Result.Nodes.getNodeAs<MaterializeTemporaryExpr>(
"temporary_expr");
321 const CXXMemberCallExpr *Call = [&]() {
329 return PushFrontCall;
334 assert(Call &&
"No call matched");
335 assert((CtorCall || MakeCall) &&
"No push_back parameter matched");
337 if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs() >= 1 &&
338 CtorCall->getArg(0)->getSourceRange() == CtorCall->getSourceRange())
341 const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
342 Call->getExprLoc(), Call->getArg(0)->getExprLoc());
346 ?
diag(TemporaryExpr ? TemporaryExpr->getBeginLoc()
347 : CtorCall ? CtorCall->getBeginLoc()
348 : MakeCall->getBeginLoc(),
349 "unnecessary temporary object created while calling %0")
350 :
diag(Call->getExprLoc(),
"use emplace%select{|_back|_front}0 "
351 "instead of push%select{|_back|_front}0");
353 Diag << Call->getMethodDecl()->getName();
356 else if (PushBackCall)
361 if (FunctionNameSourceRange.getBegin().isMacroID())
365 const char *EmplacePrefix = MakeCall ?
"emplace_back" :
"emplace_back(";
366 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
368 }
else if (PushCall) {
369 const char *EmplacePrefix = MakeCall ?
"emplace" :
"emplace(";
370 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
372 }
else if (PushFrontCall) {
373 const char *EmplacePrefix = MakeCall ?
"emplace_front" :
"emplace_front(";
374 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
378 const SourceRange CallParensRange =
379 MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(),
380 MakeCall->getRParenLoc())
381 : CtorCall->getParenOrBraceRange();
384 if (CallParensRange.getBegin().isInvalid())
388 const SourceLocation ExprBegin = TemporaryExpr ? TemporaryExpr->getExprLoc()
389 : CtorCall ? CtorCall->getExprLoc()
390 : MakeCall->getExprLoc();
393 const auto ParamCallSourceRange =
394 CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
397 const auto EndCallSourceRange = CharSourceRange::getTokenRange(
398 CallParensRange.getEnd(),
399 TemporaryExpr ? TemporaryExpr->getEndLoc() : CallParensRange.getEnd());
401 Diag << FixItHint::CreateRemoval(ParamCallSourceRange)
402 << FixItHint::CreateRemoval(EndCallSourceRange);
404 if (MakeCall && EmplacyCall) {
406 Diag << FixItHint::CreateRemoval(
407 CharSourceRange::getCharRange(MakeCall->getCallee()->getEndLoc(),
408 MakeCall->getArg(0)->getBeginLoc()));
413 Options.
store(Opts,
"IgnoreImplicitConstructors", IgnoreImplicitConstructors);
llvm::SmallString< 256U > Name
CodeCompletionBuilder Builder
::clang::DynTypedNode Node
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.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap