1 //===--- MoveConstructorInitCheck.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 "MoveConstructorInitCheck.h" 10 #include "../utils/Matchers.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace performance { 19 20 MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name, 21 ClangTidyContext *Context) 22 : ClangTidyCheck(Name, Context) {} 23 24 void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) { 25 Finder->addMatcher( 26 traverse(TK_AsIs, 27 cxxConstructorDecl( 28 unless(isImplicit()), isMoveConstructor(), 29 hasAnyConstructorInitializer( 30 cxxCtorInitializer( 31 withInitializer(cxxConstructExpr(hasDeclaration( 32 cxxConstructorDecl(isCopyConstructor()) 33 .bind("ctor"))))) 34 .bind("move-init")))), 35 this); 36 } 37 38 void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) { 39 const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor"); 40 const auto *Initializer = 41 Result.Nodes.getNodeAs<CXXCtorInitializer>("move-init"); 42 43 // Do not diagnose if the expression used to perform the initialization is a 44 // trivially-copyable type. 45 QualType QT = Initializer->getInit()->getType(); 46 if (QT.isTriviallyCopyableType(*Result.Context)) 47 return; 48 49 if (QT.isConstQualified()) 50 return; 51 52 const auto *RD = QT->getAsCXXRecordDecl(); 53 if (RD && RD->isTriviallyCopyable()) 54 return; 55 56 // Diagnose when the class type has a move constructor available, but the 57 // ctor-initializer uses the copy constructor instead. 58 const CXXConstructorDecl *Candidate = nullptr; 59 for (const auto *Ctor : CopyCtor->getParent()->ctors()) { 60 if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected && 61 !Ctor->isDeleted()) { 62 // The type has a move constructor that is at least accessible to the 63 // initializer. 64 // 65 // FIXME: Determine whether the move constructor is a viable candidate 66 // for the ctor-initializer, perhaps provide a fix-it that suggests 67 // using std::move(). 68 Candidate = Ctor; 69 break; 70 } 71 } 72 73 if (Candidate) { 74 // There's a move constructor candidate that the caller probably intended 75 // to call instead. 76 diag(Initializer->getSourceLocation(), 77 "move constructor initializes %select{class member|base class}0 by " 78 "calling a copy constructor") 79 << Initializer->isBaseInitializer(); 80 diag(CopyCtor->getLocation(), "copy constructor being called", 81 DiagnosticIDs::Note); 82 diag(Candidate->getLocation(), "candidate move constructor here", 83 DiagnosticIDs::Note); 84 } 85 } 86 87 } // namespace performance 88 } // namespace tidy 89 } // namespace clang 90