1 //===--- CopyConstructorInitCheck.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 "CopyConstructorInitCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::bugprone
{
18 void CopyConstructorInitCheck::registerMatchers(MatchFinder
*Finder
) {
19 // In the future this might be extended to move constructors?
23 hasAnyConstructorInitializer(cxxCtorInitializer(
25 withInitializer(cxxConstructExpr(hasDeclaration(
26 cxxConstructorDecl(isDefaultConstructor())))))),
27 unless(isInstantiated()))
32 void CopyConstructorInitCheck::check(const MatchFinder::MatchResult
&Result
) {
33 const auto *Ctor
= Result
.Nodes
.getNodeAs
<CXXConstructorDecl
>("ctor");
34 std::string ParamName
= Ctor
->getParamDecl(0)->getNameAsString();
36 // We want only one warning (and FixIt) for each ctor.
37 std::string FixItInitList
;
38 bool HasRelevantBaseInit
= false;
39 bool ShouldNotDoFixit
= false;
40 bool HasWrittenInitializer
= false;
41 SmallVector
<FixItHint
, 2> SafeFixIts
;
42 for (const auto *Init
: Ctor
->inits()) {
43 bool CtorInitIsWritten
= Init
->isWritten();
44 HasWrittenInitializer
= HasWrittenInitializer
|| CtorInitIsWritten
;
45 if (!Init
->isBaseInitializer())
47 const Type
*BaseType
= Init
->getBaseClass();
48 // Do not do fixits if there is a type alias involved or one of the bases
49 // are explicitly initialized. In the latter case we not do fixits to avoid
50 // -Wreorder warnings.
51 if (const auto *TempSpecTy
= dyn_cast
<TemplateSpecializationType
>(BaseType
))
52 ShouldNotDoFixit
= ShouldNotDoFixit
|| TempSpecTy
->isTypeAlias();
53 ShouldNotDoFixit
= ShouldNotDoFixit
|| isa
<TypedefType
>(BaseType
);
54 ShouldNotDoFixit
= ShouldNotDoFixit
|| CtorInitIsWritten
;
55 const CXXRecordDecl
*BaseClass
=
56 BaseType
->getAsCXXRecordDecl()->getDefinition();
57 if (BaseClass
->field_empty() &&
58 BaseClass
->forallBases(
59 [](const CXXRecordDecl
*Class
) { return Class
->field_empty(); }))
61 bool NonCopyableBase
= false;
62 for (const auto *Ctor
: BaseClass
->ctors()) {
63 if (Ctor
->isCopyConstructor() &&
64 (Ctor
->getAccess() == AS_private
|| Ctor
->isDeleted())) {
65 NonCopyableBase
= true;
71 const auto *CExpr
= dyn_cast
<CXXConstructExpr
>(Init
->getInit());
72 if (!CExpr
|| !CExpr
->getConstructor()->isDefaultConstructor())
74 HasRelevantBaseInit
= true;
75 if (CtorInitIsWritten
) {
76 if (!ParamName
.empty())
78 FixItHint::CreateInsertion(CExpr
->getEndLoc(), ParamName
));
80 if (Init
->getSourceLocation().isMacroID() ||
81 Ctor
->getLocation().isMacroID() || ShouldNotDoFixit
)
83 FixItInitList
+= BaseClass
->getNameAsString();
84 FixItInitList
+= "(" + ParamName
+ "), ";
87 if (!HasRelevantBaseInit
)
90 auto Diag
= diag(Ctor
->getLocation(),
91 "calling a base constructor other than the copy constructor")
94 if (FixItInitList
.empty() || ParamName
.empty() || ShouldNotDoFixit
)
97 std::string FixItMsg
{FixItInitList
.substr(0, FixItInitList
.size() - 2)};
98 SourceLocation FixItLoc
;
99 // There is no initialization list in this constructor.
100 if (!HasWrittenInitializer
) {
101 FixItLoc
= Ctor
->getBody()->getBeginLoc();
102 FixItMsg
= " : " + FixItMsg
;
104 // We apply the missing ctors at the beginning of the initialization list.
105 FixItLoc
= (*Ctor
->init_begin())->getSourceLocation();
110 Diag
<< FixItHint::CreateInsertion(FixItLoc
, FixItMsg
);
113 } // namespace clang::tidy::bugprone