blob: 0ba0f165114912386ce91a5e616a6bd9b453c637 [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(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000026 cxxConstructorDecl(
27 unless(isImplicit()),
28 allOf(isMoveConstructor(),
29 hasAnyConstructorInitializer(
30 cxxCtorInitializer(
31 withInitializer(cxxConstructExpr(hasDeclaration(
32 cxxConstructorDecl(isCopyConstructor())
33 .bind("ctor")))))
34 .bind("init")))),
35 this);
Aaron Ballman9392ced2015-08-20 15:52:52 +000036}
37
38void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
39 const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
40 const auto *Initializer = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
41
42 // Do not diagnose if the expression used to perform the initialization is a
43 // trivially-copyable type.
44 QualType QT = Initializer->getInit()->getType();
45 if (QT.isTriviallyCopyableType(*Result.Context))
46 return;
47
48 const auto *RD = QT->getAsCXXRecordDecl();
49 if (RD && RD->isTriviallyCopyable())
50 return;
51
52 // Diagnose when the class type has a move constructor available, but the
53 // ctor-initializer uses the copy constructor instead.
54 const CXXConstructorDecl *Candidate = nullptr;
55 for (const auto *Ctor : CopyCtor->getParent()->ctors()) {
56 if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected &&
57 !Ctor->isDeleted()) {
58 // The type has a move constructor that is at least accessible to the
59 // initializer.
60 //
61 // FIXME: Determine whether the move constructor is a viable candidate
62 // for the ctor-initializer, perhaps provide a fixit that suggests
63 // using std::move().
64 Candidate = Ctor;
65 break;
66 }
67 }
68
69 if (Candidate) {
70 // There's a move constructor candidate that the caller probably intended
71 // to call instead.
72 diag(Initializer->getSourceLocation(),
73 "move constructor initializes %0 by calling a copy constructor")
74 << (Initializer->isBaseInitializer() ? "base class" : "class member");
75 diag(CopyCtor->getLocation(), "copy constructor being called",
76 DiagnosticIDs::Note);
77 diag(Candidate->getLocation(), "candidate move constructor here",
78 DiagnosticIDs::Note);
79 }
80}
81
82} // namespace tidy
83} // namespace clang
84