llvm-project
113 строк · 4.2 Кб
1//===--- CopyConstructorInitCheck.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 "CopyConstructorInitCheck.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/Lexer.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang::tidy::bugprone {
17
18void CopyConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
19// In the future this might be extended to move constructors?
20Finder->addMatcher(
21cxxConstructorDecl(
22isCopyConstructor(),
23hasAnyConstructorInitializer(cxxCtorInitializer(
24isBaseInitializer(),
25withInitializer(cxxConstructExpr(hasDeclaration(
26cxxConstructorDecl(isDefaultConstructor())))))),
27unless(isInstantiated()))
28.bind("ctor"),
29this);
30}
31
32void CopyConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
33const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
34std::string ParamName = Ctor->getParamDecl(0)->getNameAsString();
35
36// We want only one warning (and FixIt) for each ctor.
37std::string FixItInitList;
38bool HasRelevantBaseInit = false;
39bool ShouldNotDoFixit = false;
40bool HasWrittenInitializer = false;
41SmallVector<FixItHint, 2> SafeFixIts;
42for (const auto *Init : Ctor->inits()) {
43bool CtorInitIsWritten = Init->isWritten();
44HasWrittenInitializer = HasWrittenInitializer || CtorInitIsWritten;
45if (!Init->isBaseInitializer())
46continue;
47const 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.
51if (const auto *TempSpecTy = dyn_cast<TemplateSpecializationType>(BaseType))
52ShouldNotDoFixit = ShouldNotDoFixit || TempSpecTy->isTypeAlias();
53ShouldNotDoFixit = ShouldNotDoFixit || isa<TypedefType>(BaseType);
54ShouldNotDoFixit = ShouldNotDoFixit || CtorInitIsWritten;
55const CXXRecordDecl *BaseClass =
56BaseType->getAsCXXRecordDecl()->getDefinition();
57if (BaseClass->field_empty() &&
58BaseClass->forallBases(
59[](const CXXRecordDecl *Class) { return Class->field_empty(); }))
60continue;
61bool NonCopyableBase = false;
62for (const auto *Ctor : BaseClass->ctors()) {
63if (Ctor->isCopyConstructor() &&
64(Ctor->getAccess() == AS_private || Ctor->isDeleted())) {
65NonCopyableBase = true;
66break;
67}
68}
69if (NonCopyableBase)
70continue;
71const auto *CExpr = dyn_cast<CXXConstructExpr>(Init->getInit());
72if (!CExpr || !CExpr->getConstructor()->isDefaultConstructor())
73continue;
74HasRelevantBaseInit = true;
75if (CtorInitIsWritten) {
76if (!ParamName.empty())
77SafeFixIts.push_back(
78FixItHint::CreateInsertion(CExpr->getEndLoc(), ParamName));
79} else {
80if (Init->getSourceLocation().isMacroID() ||
81Ctor->getLocation().isMacroID() || ShouldNotDoFixit)
82break;
83FixItInitList += BaseClass->getNameAsString();
84FixItInitList += "(" + ParamName + "), ";
85}
86}
87if (!HasRelevantBaseInit)
88return;
89
90auto Diag = diag(Ctor->getLocation(),
91"calling a base constructor other than the copy constructor")
92<< SafeFixIts;
93
94if (FixItInitList.empty() || ParamName.empty() || ShouldNotDoFixit)
95return;
96
97std::string FixItMsg{FixItInitList.substr(0, FixItInitList.size() - 2)};
98SourceLocation FixItLoc;
99// There is no initialization list in this constructor.
100if (!HasWrittenInitializer) {
101FixItLoc = Ctor->getBody()->getBeginLoc();
102FixItMsg = " : " + FixItMsg;
103} else {
104// We apply the missing ctors at the beginning of the initialization list.
105FixItLoc = (*Ctor->init_begin())->getSourceLocation();
106FixItMsg += ',';
107}
108FixItMsg += ' ';
109
110Diag << FixItHint::CreateInsertion(FixItLoc, FixItMsg);
111}
112
113} // namespace clang::tidy::bugprone
114