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