1 //===--- UndelegatedConstructorCheck.cpp - clang-tidy --------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "UndelegatedConstructorCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/Lex/Lexer.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::bugprone
{
18 AST_MATCHER_P(Stmt
, ignoringTemporaryExpr
,
19 ast_matchers::internal::Matcher
<Stmt
>, InnerMatcher
) {
20 const Stmt
*E
= &Node
;
22 // Temporaries with non-trivial dtors.
23 if (const auto *EWC
= dyn_cast
<ExprWithCleanups
>(E
))
24 E
= EWC
->getSubExpr();
25 // Temporaries with zero or more than two ctor arguments.
26 else if (const auto *BTE
= dyn_cast
<CXXBindTemporaryExpr
>(E
))
27 E
= BTE
->getSubExpr();
28 // Temporaries with exactly one ctor argument.
29 else if (const auto *FCE
= dyn_cast
<CXXFunctionalCastExpr
>(E
))
30 E
= FCE
->getSubExpr();
35 return InnerMatcher
.matches(*E
, Finder
, Builder
);
38 // Finds a node if it's a base of an already bound node.
39 AST_MATCHER_P(CXXRecordDecl
, baseOfBoundNode
, std::string
, ID
) {
40 return Builder
->removeBindings(
41 [&](const ast_matchers::internal::BoundNodesMap
&Nodes
) {
42 const auto *Derived
= Nodes
.getNodeAs
<CXXRecordDecl
>(ID
);
43 return Derived
!= &Node
&& !Derived
->isDerivedFrom(&Node
);
48 void UndelegatedConstructorCheck::registerMatchers(MatchFinder
*Finder
) {
49 // We look for calls to constructors of the same type in constructors. To do
50 // this we have to look through a variety of nodes that occur in the path,
51 // depending on the type's destructor and the number of arguments on the
52 // constructor call, this is handled by ignoringTemporaryExpr. Ignore template
53 // instantiations to reduce the number of duplicated warnings.
58 compoundStmt(hasParent(cxxConstructorDecl(
59 ofClass(cxxRecordDecl().bind("parent")))),
60 forEach(ignoringTemporaryExpr(
62 hasDeclaration(cxxConstructorDecl(ofClass(
63 cxxRecordDecl(baseOfBoundNode("parent"))))))
65 unless(isInTemplateInstantiation()))),
69 void UndelegatedConstructorCheck::check(
70 const MatchFinder::MatchResult
&Result
) {
71 const auto *E
= Result
.Nodes
.getNodeAs
<CXXConstructExpr
>("construct");
72 diag(E
->getBeginLoc(), "did you intend to call a delegated constructor? "
73 "A temporary object is created here instead");
76 } // namespace clang::tidy::bugprone