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