11 #include "../utils/Matchers.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Frontend/CompilerInstance.h"
15 #include "clang/Lex/Lexer.h"
16 #include "clang/Lex/Preprocessor.h"
18 using namespace clang::ast_matchers;
26 parmVarDeclRefExprOccurences(
const ParmVarDecl &MovableParam,
27 const CXXConstructorDecl &ConstructorDecl,
29 unsigned int Occurrences = 0;
31 findAll(declRefExpr(to(parmVarDecl(equalsNode(&MovableParam)))));
32 Occurrences += match(AllDeclRefs, *ConstructorDecl.getBody(),
Context).size();
33 for (
const auto *Initializer : ConstructorDecl.inits()) {
34 Occurrences += match(AllDeclRefs, *Initializer->getInit(),
Context).size();
41 MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef
Name,
45 Options.get(
"IncludeStyle",
"llvm"))),
46 UseCERTSemantics(Context->isCheckEnabled(
"cert-oop11-cpp")) {}
57 allOf(isMoveConstructor(),
58 hasAnyConstructorInitializer(
60 withInitializer(cxxConstructExpr(hasDeclaration(
61 cxxConstructorDecl(isCopyConstructor())
63 .bind(
"move-init")))),
66 auto NonConstValueMovableAndExpensiveToCopy =
67 qualType(allOf(unless(pointerType()), unless(isConstQualified()),
68 hasDeclaration(cxxRecordDecl(hasMethod(cxxConstructorDecl(
69 isMoveConstructor(), unless(isDeleted()))))),
74 if (!UseCERTSemantics)
78 unless(isMoveConstructor()),
79 hasAnyConstructorInitializer(withInitializer(cxxConstructExpr(
80 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
86 NonConstValueMovableAndExpensiveToCopy))
87 .bind(
"movable-param")))
88 .bind(
"init-arg")))))))
94 if (Result.Nodes.getNodeAs<CXXCtorInitializer>(
"move-init") !=
nullptr)
95 handleMoveConstructor(Result);
96 if (Result.Nodes.getNodeAs<ParmVarDecl>(
"movable-param") !=
nullptr)
97 handleParamNotMoved(Result);
100 void MoveConstructorInitCheck::handleParamNotMoved(
101 const MatchFinder::MatchResult &
Result) {
102 const auto *MovableParam =
103 Result.Nodes.getNodeAs<ParmVarDecl>(
"movable-param");
104 const auto *ConstructorDecl =
105 Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor-decl");
106 const auto *InitArg = Result.Nodes.getNodeAs<DeclRefExpr>(
"init-arg");
108 if (parmVarDeclRefExprOccurences(*MovableParam, *ConstructorDecl,
109 *Result.Context) > 1)
112 diag(InitArg->getLocStart(),
"value argument can be moved to avoid copy");
113 DiagOut << FixItHint::CreateReplacement(
114 InitArg->getSourceRange(),
115 (Twine(
"std::move(") + MovableParam->getName() +
")").str());
116 if (
auto IncludeFixit = Inserter->CreateIncludeInsertion(
117 Result.SourceManager->getFileID(InitArg->getLocStart()),
"utility",
119 DiagOut << *IncludeFixit;
123 void MoveConstructorInitCheck::handleMoveConstructor(
124 const MatchFinder::MatchResult &Result) {
125 const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor");
126 const auto *Initializer = Result.Nodes.getNodeAs<CXXCtorInitializer>(
"move-init");
130 QualType QT = Initializer->getInit()->getType();
131 if (QT.isTriviallyCopyableType(*Result.Context))
134 const auto *RD = QT->getAsCXXRecordDecl();
135 if (RD && RD->isTriviallyCopyable())
140 const CXXConstructorDecl *Candidate =
nullptr;
141 for (
const auto *Ctor : CopyCtor->getParent()->ctors()) {
142 if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected &&
143 !Ctor->isDeleted()) {
158 diag(Initializer->getSourceLocation(),
159 "move constructor initializes %0 by calling a copy constructor")
160 << (Initializer->isBaseInitializer() ?
"base class" :
"class member");
161 diag(CopyCtor->getLocation(),
"copy constructor being called",
162 DiagnosticIDs::Note);
163 diag(Candidate->getLocation(),
"candidate move constructor here",
164 DiagnosticIDs::Note);
170 Compiler.getLangOpts(), IncludeStyle));
171 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
LangOptions getLangOpts() const
Returns the language options from the context.
std::unique_ptr< ast_matchers::MatchFinder > Finder
static StringRef toString(IncludeStyle Style)
Base class for all clang-tidy checks.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
std::map< std::string, std::string > OptionMap
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
llvm::Optional< bool > isExpensiveToCopy(QualType Type, ASTContext &Context)
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerPPCallbacks(clang::CompilerInstance &Compiler) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.