10#include "../utils/OptionsUtils.h"
20AST_MATCHER_P(NamedDecl, hasAnyNameIgnoringTemplates, std::vector<StringRef>,
22 const std::string FullName =
"::" +
Node.getQualifiedNameAsString();
27 std::string FullNameTrimmed;
29 for (
const auto &Character : FullName) {
30 if (Character ==
'<') {
32 }
else if (Character ==
'>') {
34 }
else if (Depth == 0) {
35 FullNameTrimmed.append(1, Character);
42 const StringRef FullNameTrimmedRef = FullNameTrimmed;
43 for (
const StringRef Pattern : Names) {
44 if (Pattern.startswith(
"::")) {
45 if (FullNameTrimmed == Pattern)
47 }
else if (FullNameTrimmedRef.endswith(Pattern) &&
48 FullNameTrimmedRef.drop_back(Pattern.size()).endswith(
"::")) {
58 clang::ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
59 if (
Node.getNumArgs() == 0)
62 return InnerMatcher.matches(*
Node.getArg(
Node.getNumArgs() - 1), Finder,
69AST_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();
78 return Node.hasExplicitTemplateArgs();
84auto hasTypeOrPointeeType(
85 const ast_matchers::internal::Matcher<QualType> &TypeMatcher) {
86 return anyOf(hasType(TypeMatcher),
87 hasType(pointerType(pointee(TypeMatcher))));
91auto hasWantedType(llvm::ArrayRef<StringRef> TypeNames) {
92 return hasCanonicalType(hasDeclaration(cxxRecordDecl(hasAnyName(TypeNames))));
97auto cxxMemberCallExprOnContainer(
98 StringRef MethodName, llvm::ArrayRef<StringRef> ContainerNames) {
99 return cxxMemberCallExpr(
100 hasDeclaration(functionDecl(hasName(MethodName))),
101 on(hasTypeOrPointeeType(hasWantedType(ContainerNames))));
104const auto DefaultContainersWithPushBack =
105 "::std::vector; ::std::list; ::std::deque";
106const auto DefaultContainersWithPush =
107 "::std::stack; ::std::queue; ::std::priority_queue";
108const auto DefaultContainersWithPushFront =
109 "::std::forward_list; ::std::list; ::std::deque";
110const auto DefaultSmartPointers =
111 "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
112const auto DefaultTupleTypes =
"::std::pair; ::std::tuple";
113const auto DefaultTupleMakeFunctions =
"::std::make_pair; ::std::make_tuple";
114const 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";
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))) {}
155 cxxMemberCallExprOnContainer(
"push_back", ContainersWithPushBack);
156 auto CallPush = cxxMemberCallExprOnContainer(
"push", ContainersWithPush);
158 cxxMemberCallExprOnContainer(
"push_front", ContainersWithPushFront);
160 auto CallEmplacy = cxxMemberCallExpr(
162 functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))),
163 on(hasTypeOrPointeeType(hasCanonicalType(hasDeclaration(
164 has(typedefNameDecl(hasName(
"value_type"),
165 hasType(type(hasUnqualifiedDesugaredType(
166 recordType().bind(
"value_type")))))))))));
172 auto IsCtorOfSmartPtr =
173 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(SmartPointers))));
176 auto BitFieldAsArgument = hasAnyArgument(
177 ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
180 auto InitializerListAsArgument = hasAnyArgument(
181 ignoringImplicit(allOf(cxxConstructExpr(isListInitialization()),
182 unless(cxxTemporaryObjectExpr()))));
185 auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
187 auto ConstructingDerived =
188 hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
191 auto IsPrivateOrProtectedCtor =
192 hasDeclaration(cxxConstructorDecl(anyOf(isPrivate(), isProtected())));
194 auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())),
195 has(cxxStdInitializerListExpr()));
199 auto SoughtConstructExpr =
201 unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
202 InitializerListAsArgument, NewExprAsArgument,
203 ConstructingDerived, IsPrivateOrProtectedCtor)))
205 auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
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))))))))
225 auto MakeTupleCtor = ignoringImplicit(cxxConstructExpr(
226 has(materializeTemporaryExpr(MakeTuple)),
227 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(TupleTypes))))));
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"));
260 traverse(TK_AsIs, cxxMemberCallExpr(CallPushBack, has(SoughtParam),
261 unless(isInTemplateInstantiation()))
262 .bind(
"push_back_call")),
266 traverse(TK_AsIs, cxxMemberCallExpr(CallPush, has(SoughtParam),
267 unless(isInTemplateInstantiation()))
272 traverse(TK_AsIs, cxxMemberCallExpr(CallPushFront, has(SoughtParam),
273 unless(isInTemplateInstantiation()))
274 .bind(
"push_front_call")),
280 CallEmplacy, HasConstructExprWithValueTypeTypeAsLastArgument,
281 hasSameNumArgsAsDeclNumParams(),
282 unless(isInTemplateInstantiation()))
283 .bind(
"emplacy_call")),
291 on(hasType(cxxRecordDecl(has(typedefNameDecl(
292 hasName(
"value_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")),
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 = [&]() {
324 return PushFrontCall;
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())
336 const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
337 Call->getExprLoc(), Call->getArg(0)->getExprLoc());
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");
348 Diag << Call->getMethodDecl()->getName();
351 else if (PushBackCall)
356 if (FunctionNameSourceRange.getBegin().isMacroID())
360 const char *EmplacePrefix = MakeCall ?
"emplace_back" :
"emplace_back(";
361 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
363 }
else if (PushCall) {
364 const char *EmplacePrefix = MakeCall ?
"emplace" :
"emplace(";
365 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
367 }
else if (PushFrontCall) {
368 const char *EmplacePrefix = MakeCall ?
"emplace_front" :
"emplace_front(";
369 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
373 const SourceRange CallParensRange =
374 MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(),
375 MakeCall->getRParenLoc())
376 : CtorCall->getParenOrBraceRange();
379 if (CallParensRange.getBegin().isInvalid())
383 const SourceLocation ExprBegin = TemporaryExpr ? TemporaryExpr->getExprLoc()
384 : CtorCall ? CtorCall->getExprLoc()
385 : MakeCall->getExprLoc();
388 const auto ParamCallSourceRange =
389 CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
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) {
401 Diag << FixItHint::CreateRemoval(
402 CharSourceRange::getCharRange(MakeCall->getCallee()->getEndLoc(),
403 MakeCall->getArg(0)->getBeginLoc()));
408 Options.
store(Opts,
"IgnoreImplicitConstructors", IgnoreImplicitConstructors);
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