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 #include "clang/Frontend/CompilerInstance.h" 14 #include "clang/Lex/Lexer.h" 15 #include "clang/Lex/Preprocessor.h" 16 17 using namespace clang::ast_matchers; 18 19 namespace clang { 20 namespace tidy { 21 namespace performance { 22 23 MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name, 24 ClangTidyContext *Context) 25 : ClangTidyCheck(Name, Context), 26 IncludeStyle(utils::IncludeSorter::parseIncludeStyle( 27 Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {} 28 29 void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) { 30 // Only register the matchers for C++11; the functionality currently does not 31 // provide any benefit to other languages, despite being benign. 32 if (!getLangOpts().CPlusPlus11) 33 return; 34 35 Finder->addMatcher( 36 cxxConstructorDecl( 37 unless(isImplicit()), isMoveConstructor(), 38 hasAnyConstructorInitializer( 39 cxxCtorInitializer( 40 withInitializer(cxxConstructExpr(hasDeclaration( 41 cxxConstructorDecl(isCopyConstructor()).bind("ctor"))))) 42 .bind("move-init"))), 43 this); 44 } 45 46 void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) { 47 const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor"); 48 const auto *Initializer = 49 Result.Nodes.getNodeAs<CXXCtorInitializer>("move-init"); 50 51 // Do not diagnose if the expression used to perform the initialization is a 52 // trivially-copyable type. 53 QualType QT = Initializer->getInit()->getType(); 54 if (QT.isTriviallyCopyableType(*Result.Context)) 55 return; 56 57 if (QT.isConstQualified()) 58 return; 59 60 const auto *RD = QT->getAsCXXRecordDecl(); 61 if (RD && RD->isTriviallyCopyable()) 62 return; 63 64 // Diagnose when the class type has a move constructor available, but the 65 // ctor-initializer uses the copy constructor instead. 66 const CXXConstructorDecl *Candidate = nullptr; 67 for (const auto *Ctor : CopyCtor->getParent()->ctors()) { 68 if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected && 69 !Ctor->isDeleted()) { 70 // The type has a move constructor that is at least accessible to the 71 // initializer. 72 // 73 // FIXME: Determine whether the move constructor is a viable candidate 74 // for the ctor-initializer, perhaps provide a fixit that suggests 75 // using std::move(). 76 Candidate = Ctor; 77 break; 78 } 79 } 80 81 if (Candidate) { 82 // There's a move constructor candidate that the caller probably intended 83 // to call instead. 84 diag(Initializer->getSourceLocation(), 85 "move constructor initializes %0 by calling a copy constructor") 86 << (Initializer->isBaseInitializer() ? "base class" : "class member"); 87 diag(CopyCtor->getLocation(), "copy constructor being called", 88 DiagnosticIDs::Note); 89 diag(Candidate->getLocation(), "candidate move constructor here", 90 DiagnosticIDs::Note); 91 } 92 } 93 94 void MoveConstructorInitCheck::registerPPCallbacks( 95 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { 96 Inserter = llvm::make_unique<utils::IncludeInserter>(SM, getLangOpts(), 97 IncludeStyle); 98 PP->addPPCallbacks(Inserter->CreatePPCallbacks()); 99 } 100 101 void MoveConstructorInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 102 Options.store(Opts, "IncludeStyle", 103 utils::IncludeSorter::toString(IncludeStyle)); 104 } 105 106 } // namespace performance 107 } // namespace tidy 108 } // namespace clang 109