1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
//===--- UseEmplaceCheck.cpp - clang-tidy----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "UseEmplaceCheck.h"
#include "../utils/OptionsUtils.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace modernize {
static const auto DefaultContainersWithPushBack =
"::std::vector; ::std::list; ::std::deque";
static const auto DefaultSmartPointers =
"::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
namespace {
namespace impl {
// FIXME: This matcher should be replaced by a matcher from ASTMatcher.h
const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
CXXStdInitializerListExpr> cxxStdInitializerListExpr;
} // namespace impl
} // namespace
UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
ContainersWithPushBack(utils::options::parseStringList(Options.get(
"ContainersWithPushBack", DefaultContainersWithPushBack))),
SmartPointers(utils::options::parseStringList(
Options.get("SmartPointers", DefaultSmartPointers))) {}
void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus11)
return;
// FIXME: Bunch of functionality that could be easily added:
// + add handling of `push_front` for std::forward_list, std::list
// and std::deque.
// + add handling of `push` for std::stack, std::queue, std::priority_queue
// + add handling of `insert` for stl associative container, but be careful
// because this requires special treatment (it could cause performance
// regression)
// + match for emplace calls that should be replaced with insertion
// + match for make_pair calls.
auto callPushBack = cxxMemberCallExpr(
hasDeclaration(functionDecl(hasName("push_back"))),
on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
ContainersWithPushBack.begin(), ContainersWithPushBack.end()))))));
// We can't replace push_backs of smart pointer because
// if emplacement fails (f.e. bad_alloc in vector) we will have leak of
// passed pointer because smart pointer won't be constructed
// (and destructed) as in push_back case.
auto isCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
SmallVector<StringRef, 5>(SmartPointers.begin(), SmartPointers.end())))));
// Bitfields binds only to consts and emplace_back take it by universal ref.
auto bitFieldAsArgument = hasAnyArgument(
ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
// Initializer list can't be passed to universal reference.
auto initializerListAsArgument = hasAnyArgument(
ignoringImplicit(cxxConstructExpr(isListInitialization())));
// We could have leak of resource.
auto newExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
// We would call another constructor.
auto constructingDerived =
hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
// emplace_back can't access private constructor.
auto isPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate()));
auto hasInitList = anyOf(has(ignoringImplicit(initListExpr())),
has(impl::cxxStdInitializerListExpr()));
// FIXME: Replace internal C++ initializer list matcher with one from
// ASTMatchers.h
// FIXME: Discard 0/NULL (as nullptr), static inline const data members,
// overloaded functions and template names.
auto soughtConstructExpr =
cxxConstructExpr(
unless(anyOf(isCtorOfSmartPtr, hasInitList, bitFieldAsArgument,
initializerListAsArgument, newExprAsArgument,
constructingDerived, isPrivateCtor)))
.bind("ctor");
auto hasConstructExpr = has(ignoringImplicit(soughtConstructExpr));
auto ctorAsArgument = materializeTemporaryExpr(
anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr))));
Finder->addMatcher(cxxMemberCallExpr(callPushBack, has(ctorAsArgument),
unless(isInTemplateInstantiation()))
.bind("call"),
this);
}
void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
const auto *InnerCtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
auto FunctionNameSourceRange = CharSourceRange::getCharRange(
Call->getExprLoc(), Call->getArg(0)->getExprLoc());
auto Diag = diag(Call->getExprLoc(), "use emplace_back instead of push_back");
if (FunctionNameSourceRange.getBegin().isMacroID())
return;
Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
"emplace_back(");
auto CallParensRange = InnerCtorCall->getParenOrBraceRange();
// Finish if there is no explicit constructor call.
if (CallParensRange.getBegin().isInvalid())
return;
// Range for constructor name and opening brace.
auto CtorCallSourceRange = CharSourceRange::getTokenRange(
InnerCtorCall->getExprLoc(), CallParensRange.getBegin());
Diag << FixItHint::CreateRemoval(CtorCallSourceRange)
<< FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
CallParensRange.getEnd(), CallParensRange.getEnd()));
}
void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "ContainersWithPushBack",
utils::options::serializeStringList(ContainersWithPushBack));
Options.store(Opts, "SmartPointers",
utils::options::serializeStringList(SmartPointers));
}
} // namespace modernize
} // namespace tidy
} // namespace clang
|