blob: 2c6af5f854c00128f1de956793f7cc260cc7b071 [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"
Aaron Ballmanfc4e0422015-10-06 16:27:03 +000011#include "../utils/Matchers.h"
Aaron Ballman9392ced2015-08-20 15:52:52 +000012#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
Aaron Ballmanfc4e0422015-10-06 16:27:03 +000014#include "clang/Frontend/CompilerInstance.h"
15#include "clang/Lex/Lexer.h"
16#include "clang/Lex/Preprocessor.h"
Aaron Ballman9392ced2015-08-20 15:52:52 +000017
18using namespace clang::ast_matchers;
19
20namespace clang {
21namespace tidy {
Etienne Bergeron456177b2016-05-02 18:00:29 +000022namespace misc {
Aaron Ballman9392ced2015-08-20 15:52:52 +000023
Aaron Ballmanfc4e0422015-10-06 16:27:03 +000024MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name,
25 ClangTidyContext *Context)
26 : ClangTidyCheck(Name, Context),
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000027 IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
Alexander Kornienkob1c74322017-07-20 12:02:03 +000028 Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {}
Aaron Ballmanfc4e0422015-10-06 16:27:03 +000029
Aaron Ballman9392ced2015-08-20 15:52:52 +000030void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
Aaron Ballman327e97b2015-08-28 19:27:19 +000031 // Only register the matchers for C++11; the functionality currently does not
32 // provide any benefit to other languages, despite being benign.
Aaron Ballmanbf891092015-08-31 15:28:57 +000033 if (!getLangOpts().CPlusPlus11)
34 return;
35
36 Finder->addMatcher(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000037 cxxConstructorDecl(
38 unless(isImplicit()),
39 allOf(isMoveConstructor(),
40 hasAnyConstructorInitializer(
41 cxxCtorInitializer(
42 withInitializer(cxxConstructExpr(hasDeclaration(
43 cxxConstructorDecl(isCopyConstructor())
44 .bind("ctor")))))
Aaron Ballmanfc4e0422015-10-06 16:27:03 +000045 .bind("move-init")))),
46 this);
Aaron Ballman9392ced2015-08-20 15:52:52 +000047}
48
49void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
50 const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000051 const auto *Initializer =
52 Result.Nodes.getNodeAs<CXXCtorInitializer>("move-init");
Aaron Ballman9392ced2015-08-20 15:52:52 +000053
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;
Kirill Bobyrev11cea452016-08-01 12:06:18 +000059
Alexander Kornienkobb642002017-02-08 14:56:16 +000060 if (QT.isConstQualified())
61 return;
62
Aaron Ballman9392ced2015-08-20 15:52:52 +000063 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
Aaron Ballmanfc4e0422015-10-06 16:27:03 +000097void MoveConstructorInitCheck::registerPPCallbacks(CompilerInstance &Compiler) {
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000098 Inserter.reset(new utils::IncludeInserter(
99 Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
Aaron Ballmanfc4e0422015-10-06 16:27:03 +0000100 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
101}
102
103void MoveConstructorInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +0000104 Options.store(Opts, "IncludeStyle",
105 utils::IncludeSorter::toString(IncludeStyle));
Aaron Ballmanfc4e0422015-10-06 16:27:03 +0000106}
107
Etienne Bergeron456177b2016-05-02 18:00:29 +0000108} // namespace misc
Aaron Ballman9392ced2015-08-20 15:52:52 +0000109} // namespace tidy
110} // namespace clang