blob: 5e513d827480c3c49ee7be36aa5656ff4d53cc3e [file] [log] [blame]
Aaron Ballman9392ced2015-08-20 15:52:52 +00001//===--- 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 "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang {
17namespace tidy {
18
19void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
Aaron Ballman327e97b2015-08-28 19:27:19 +000020 // Only register the matchers for C++11; the functionality currently does not
21 // provide any benefit to other languages, despite being benign.
Aaron Ballmanbf891092015-08-31 15:28:57 +000022 if (!getLangOpts().CPlusPlus11)
23 return;
24
25 Finder->addMatcher(
26 constructorDecl(unless(isImplicit()), allOf(
27 isMoveConstructor(),
28 hasAnyConstructorInitializer(
29 ctorInitializer(withInitializer(constructExpr(hasDeclaration(
30 constructorDecl(isCopyConstructor()).bind("ctor")
31 )))).bind("init")
32 )
33 )), this);
Aaron Ballman9392ced2015-08-20 15:52:52 +000034}
35
36void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
37 const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
38 const auto *Initializer = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
39
40 // Do not diagnose if the expression used to perform the initialization is a
41 // trivially-copyable type.
42 QualType QT = Initializer->getInit()->getType();
43 if (QT.isTriviallyCopyableType(*Result.Context))
44 return;
45
46 const auto *RD = QT->getAsCXXRecordDecl();
47 if (RD && RD->isTriviallyCopyable())
48 return;
49
50 // Diagnose when the class type has a move constructor available, but the
51 // ctor-initializer uses the copy constructor instead.
52 const CXXConstructorDecl *Candidate = nullptr;
53 for (const auto *Ctor : CopyCtor->getParent()->ctors()) {
54 if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected &&
55 !Ctor->isDeleted()) {
56 // The type has a move constructor that is at least accessible to the
57 // initializer.
58 //
59 // FIXME: Determine whether the move constructor is a viable candidate
60 // for the ctor-initializer, perhaps provide a fixit that suggests
61 // using std::move().
62 Candidate = Ctor;
63 break;
64 }
65 }
66
67 if (Candidate) {
68 // There's a move constructor candidate that the caller probably intended
69 // to call instead.
70 diag(Initializer->getSourceLocation(),
71 "move constructor initializes %0 by calling a copy constructor")
72 << (Initializer->isBaseInitializer() ? "base class" : "class member");
73 diag(CopyCtor->getLocation(), "copy constructor being called",
74 DiagnosticIDs::Note);
75 diag(Candidate->getLocation(), "candidate move constructor here",
76 DiagnosticIDs::Note);
77 }
78}
79
80} // namespace tidy
81} // namespace clang
82