1 //===--- CopyConstructorInitCheck.cpp - clang-tidy-------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "CopyConstructorInitCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "clang/Lex/Lexer.h" 14 15 using namespace clang::ast_matchers; 16 17 namespace clang { 18 namespace tidy { 19 namespace bugprone { 20 21 void CopyConstructorInitCheck::registerMatchers(MatchFinder *Finder) { 22 if (!getLangOpts().CPlusPlus) 23 return; 24 25 // In the future this might be extended to move constructors? 26 Finder->addMatcher( 27 cxxConstructorDecl( 28 isCopyConstructor(), 29 hasAnyConstructorInitializer(cxxCtorInitializer( 30 isBaseInitializer(), 31 withInitializer(cxxConstructExpr(hasDeclaration( 32 cxxConstructorDecl(isDefaultConstructor())))))), 33 unless(isInstantiated())) 34 .bind("ctor"), 35 this); 36 } 37 38 void CopyConstructorInitCheck::check(const MatchFinder::MatchResult &Result) { 39 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor"); 40 std::string ParamName = Ctor->getParamDecl(0)->getNameAsString(); 41 42 // We want only one warning (and FixIt) for each ctor. 43 std::string FixItInitList; 44 bool HasRelevantBaseInit = false; 45 bool ShouldNotDoFixit = false; 46 bool HasWrittenInitializer = false; 47 SmallVector<FixItHint, 2> SafeFixIts; 48 for (const auto *Init : Ctor->inits()) { 49 bool CtorInitIsWritten = Init->isWritten(); 50 HasWrittenInitializer = HasWrittenInitializer || CtorInitIsWritten; 51 if (!Init->isBaseInitializer()) 52 continue; 53 const Type *BaseType = Init->getBaseClass(); 54 // Do not do fixits if there is a type alias involved or one of the bases 55 // are explicitly initialized. In the latter case we not do fixits to avoid 56 // -Wreorder warnings. 57 if (const auto *TempSpecTy = dyn_cast<TemplateSpecializationType>(BaseType)) 58 ShouldNotDoFixit = ShouldNotDoFixit || TempSpecTy->isTypeAlias(); 59 ShouldNotDoFixit = ShouldNotDoFixit || isa<TypedefType>(BaseType); 60 ShouldNotDoFixit = ShouldNotDoFixit || CtorInitIsWritten; 61 const CXXRecordDecl *BaseClass = 62 BaseType->getAsCXXRecordDecl()->getDefinition(); 63 if (BaseClass->field_empty() && 64 BaseClass->forallBases( 65 [](const CXXRecordDecl *Class) { return Class->field_empty(); })) 66 continue; 67 bool NonCopyableBase = false; 68 for (const auto *Ctor : BaseClass->ctors()) { 69 if (Ctor->isCopyConstructor() && 70 (Ctor->getAccess() == AS_private || Ctor->isDeleted())) { 71 NonCopyableBase = true; 72 break; 73 } 74 } 75 if (NonCopyableBase) 76 continue; 77 const auto *CExpr = dyn_cast<CXXConstructExpr>(Init->getInit()); 78 if (!CExpr || !CExpr->getConstructor()->isDefaultConstructor()) 79 continue; 80 HasRelevantBaseInit = true; 81 if (CtorInitIsWritten) { 82 if (!ParamName.empty()) 83 SafeFixIts.push_back( 84 FixItHint::CreateInsertion(CExpr->getLocEnd(), ParamName)); 85 } else { 86 if (Init->getSourceLocation().isMacroID() || 87 Ctor->getLocation().isMacroID() || ShouldNotDoFixit) 88 break; 89 FixItInitList += BaseClass->getNameAsString(); 90 FixItInitList += "(" + ParamName + "), "; 91 } 92 } 93 if (!HasRelevantBaseInit) 94 return; 95 96 auto Diag = diag(Ctor->getLocation(), 97 "calling a base constructor other than the copy constructor") 98 << SafeFixIts; 99 100 if (FixItInitList.empty() || ParamName.empty() || ShouldNotDoFixit) 101 return; 102 103 std::string FixItMsg{FixItInitList.substr(0, FixItInitList.size() - 2)}; 104 SourceLocation FixItLoc; 105 // There is no initialization list in this constructor. 106 if (!HasWrittenInitializer) { 107 FixItLoc = Ctor->getBody()->getLocStart(); 108 FixItMsg = " : " + FixItMsg; 109 } else { 110 // We apply the missing ctors at the beginning of the initialization list. 111 FixItLoc = (*Ctor->init_begin())->getSourceLocation(); 112 FixItMsg += ','; 113 } 114 FixItMsg += ' '; 115 116 Diag << FixItHint::CreateInsertion(FixItLoc, FixItMsg); 117 } // namespace misc 118 119 } // namespace misc 120 } // namespace tidy 121 } // namespace clang 122