xref: /llvm-project/clang-tools-extra/clang-tidy/performance/MoveConstructorInitCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
16e39e689SAlexander Kornienko //===--- MoveConstructorInitCheck.cpp - clang-tidy-------------------------===//
26e39e689SAlexander Kornienko //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66e39e689SAlexander Kornienko //
76e39e689SAlexander Kornienko //===----------------------------------------------------------------------===//
86e39e689SAlexander Kornienko 
96e39e689SAlexander Kornienko #include "MoveConstructorInitCheck.h"
106e39e689SAlexander Kornienko #include "../utils/Matchers.h"
116e39e689SAlexander Kornienko #include "clang/AST/ASTContext.h"
126e39e689SAlexander Kornienko #include "clang/ASTMatchers/ASTMatchFinder.h"
136e39e689SAlexander Kornienko 
146e39e689SAlexander Kornienko using namespace clang::ast_matchers;
156e39e689SAlexander Kornienko 
16*7d2ea6c4SCarlos Galvez namespace clang::tidy::performance {
176e39e689SAlexander Kornienko 
MoveConstructorInitCheck(StringRef Name,ClangTidyContext * Context)186e39e689SAlexander Kornienko MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name,
196e39e689SAlexander Kornienko                                                    ClangTidyContext *Context)
20e96f9ccaSNathan James     : ClangTidyCheck(Name, Context) {}
216e39e689SAlexander Kornienko 
registerMatchers(MatchFinder * Finder)226e39e689SAlexander Kornienko void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
236e39e689SAlexander Kornienko   Finder->addMatcher(
24027899daSAlexander Kornienko       traverse(TK_AsIs,
256e39e689SAlexander Kornienko                cxxConstructorDecl(
26976e0c07SAlexander Kornienko                    unless(isImplicit()), isMoveConstructor(),
276e39e689SAlexander Kornienko                    hasAnyConstructorInitializer(
286e39e689SAlexander Kornienko                        cxxCtorInitializer(
296e39e689SAlexander Kornienko                            withInitializer(cxxConstructExpr(hasDeclaration(
30a72307c3SStephen Kelly                                cxxConstructorDecl(isCopyConstructor())
31a72307c3SStephen Kelly                                    .bind("ctor")))))
32a72307c3SStephen Kelly                            .bind("move-init")))),
336e39e689SAlexander Kornienko       this);
346e39e689SAlexander Kornienko }
356e39e689SAlexander Kornienko 
check(const MatchFinder::MatchResult & Result)366e39e689SAlexander Kornienko void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
376e39e689SAlexander Kornienko   const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
386e39e689SAlexander Kornienko   const auto *Initializer =
396e39e689SAlexander Kornienko       Result.Nodes.getNodeAs<CXXCtorInitializer>("move-init");
406e39e689SAlexander Kornienko 
416e39e689SAlexander Kornienko   // Do not diagnose if the expression used to perform the initialization is a
426e39e689SAlexander Kornienko   // trivially-copyable type.
436e39e689SAlexander Kornienko   QualType QT = Initializer->getInit()->getType();
446e39e689SAlexander Kornienko   if (QT.isTriviallyCopyableType(*Result.Context))
456e39e689SAlexander Kornienko     return;
466e39e689SAlexander Kornienko 
476e39e689SAlexander Kornienko   if (QT.isConstQualified())
486e39e689SAlexander Kornienko     return;
496e39e689SAlexander Kornienko 
506e39e689SAlexander Kornienko   const auto *RD = QT->getAsCXXRecordDecl();
516e39e689SAlexander Kornienko   if (RD && RD->isTriviallyCopyable())
526e39e689SAlexander Kornienko     return;
536e39e689SAlexander Kornienko 
546e39e689SAlexander Kornienko   // Diagnose when the class type has a move constructor available, but the
556e39e689SAlexander Kornienko   // ctor-initializer uses the copy constructor instead.
566e39e689SAlexander Kornienko   const CXXConstructorDecl *Candidate = nullptr;
576e39e689SAlexander Kornienko   for (const auto *Ctor : CopyCtor->getParent()->ctors()) {
586e39e689SAlexander Kornienko     if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected &&
596e39e689SAlexander Kornienko         !Ctor->isDeleted()) {
606e39e689SAlexander Kornienko       // The type has a move constructor that is at least accessible to the
616e39e689SAlexander Kornienko       // initializer.
626e39e689SAlexander Kornienko       //
636e39e689SAlexander Kornienko       // FIXME: Determine whether the move constructor is a viable candidate
64e96f9ccaSNathan James       // for the ctor-initializer, perhaps provide a fix-it that suggests
656e39e689SAlexander Kornienko       // using std::move().
666e39e689SAlexander Kornienko       Candidate = Ctor;
676e39e689SAlexander Kornienko       break;
686e39e689SAlexander Kornienko     }
696e39e689SAlexander Kornienko   }
706e39e689SAlexander Kornienko 
716e39e689SAlexander Kornienko   if (Candidate) {
726e39e689SAlexander Kornienko     // There's a move constructor candidate that the caller probably intended
736e39e689SAlexander Kornienko     // to call instead.
746e39e689SAlexander Kornienko     diag(Initializer->getSourceLocation(),
751a721b6aSNathan James          "move constructor initializes %select{class member|base class}0 by "
761a721b6aSNathan James          "calling a copy constructor")
771a721b6aSNathan James         << Initializer->isBaseInitializer();
786e39e689SAlexander Kornienko     diag(CopyCtor->getLocation(), "copy constructor being called",
796e39e689SAlexander Kornienko          DiagnosticIDs::Note);
806e39e689SAlexander Kornienko     diag(Candidate->getLocation(), "candidate move constructor here",
816e39e689SAlexander Kornienko          DiagnosticIDs::Note);
826e39e689SAlexander Kornienko   }
836e39e689SAlexander Kornienko }
846e39e689SAlexander Kornienko 
85*7d2ea6c4SCarlos Galvez } // namespace clang::tidy::performance
86