114 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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?20 Finder->addMatcher(21 cxxConstructorDecl(22 isCopyConstructor(),23 hasAnyConstructorInitializer(cxxCtorInitializer(24 isBaseInitializer(),25 withInitializer(cxxConstructExpr(hasDeclaration(26 cxxConstructorDecl(isDefaultConstructor())))))),27 unless(isInstantiated()))28 .bind("ctor"),29 this);30}31 32void CopyConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {33 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");34 const std::string ParamName = Ctor->getParamDecl(0)->getNameAsString();35 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 const bool CtorInitIsWritten = Init->isWritten();44 HasWrittenInitializer = HasWrittenInitializer || CtorInitIsWritten;45 if (!Init->isBaseInitializer())46 continue;47 const Type *BaseType = Init->getBaseClass();48 // Do not do fixits if there is a type alias involved or one of the bases49 // are explicitly initialized. In the latter case we not do fixits to avoid50 // -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(); }))60 continue;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;66 break;67 }68 }69 if (NonCopyableBase)70 continue;71 const auto *CExpr = dyn_cast<CXXConstructExpr>(Init->getInit());72 if (!CExpr || !CExpr->getConstructor()->isDefaultConstructor())73 continue;74 HasRelevantBaseInit = true;75 if (CtorInitIsWritten) {76 if (!ParamName.empty())77 SafeFixIts.push_back(78 FixItHint::CreateInsertion(CExpr->getEndLoc(), ParamName));79 } else {80 if (Init->getSourceLocation().isMacroID() ||81 Ctor->getLocation().isMacroID() || ShouldNotDoFixit)82 break;83 FixItInitList += BaseClass->getNameAsString();84 FixItInitList += "(" + ParamName + "), ";85 }86 }87 if (!HasRelevantBaseInit)88 return;89 90 auto Diag = diag(Ctor->getLocation(),91 "calling a base constructor other than the copy constructor")92 << SafeFixIts;93 94 if (FixItInitList.empty() || ParamName.empty() || ShouldNotDoFixit)95 return;96 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;103 } else {104 // We apply the missing ctors at the beginning of the initialization list.105 FixItLoc = (*Ctor->init_begin())->getSourceLocation();106 FixItMsg += ',';107 }108 FixItMsg += ' ';109 110 Diag << FixItHint::CreateInsertion(FixItLoc, FixItMsg);111}112 113} // namespace clang::tidy::bugprone114