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