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;
27 parmVarDeclRefExprOccurences(
const ParmVarDecl &MovableParam,
28 const CXXConstructorDecl &ConstructorDecl,
30 unsigned int Occurrences = 0;
32 findAll(declRefExpr(to(parmVarDecl(equalsNode(&MovableParam)))));
33 Occurrences += match(AllDeclRefs, *ConstructorDecl.getBody(),
Context).size();
34 for (
const auto *Initializer : ConstructorDecl.inits()) {
35 Occurrences += match(AllDeclRefs, *Initializer->getInit(),
Context).size();
42 MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef
Name,
45 IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
46 Options.get(
"IncludeStyle",
"llvm"))),
47 UseCERTSemantics(Options.get(
"UseCERTSemantics", 0) != 0) {}
58 allOf(isMoveConstructor(),
59 hasAnyConstructorInitializer(
61 withInitializer(cxxConstructExpr(hasDeclaration(
62 cxxConstructorDecl(isCopyConstructor())
64 .bind(
"move-init")))),
67 auto NonConstValueMovableAndExpensiveToCopy =
68 qualType(allOf(unless(pointerType()), unless(isConstQualified()),
69 hasDeclaration(cxxRecordDecl(hasMethod(cxxConstructorDecl(
70 isMoveConstructor(), unless(isDeleted()))))),
75 if (!UseCERTSemantics) {
79 unless(isMoveConstructor()),
80 hasAnyConstructorInitializer(withInitializer(cxxConstructExpr(
81 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
87 NonConstValueMovableAndExpensiveToCopy))
88 .bind(
"movable-param")))
89 .bind(
"init-arg")))))))
96 if (Result.Nodes.getNodeAs<CXXCtorInitializer>(
"move-init") !=
nullptr)
97 handleMoveConstructor(Result);
98 if (Result.Nodes.getNodeAs<ParmVarDecl>(
"movable-param") !=
nullptr)
99 handleParamNotMoved(Result);
102 void MoveConstructorInitCheck::handleParamNotMoved(
103 const MatchFinder::MatchResult &
Result) {
104 const auto *MovableParam =
105 Result.Nodes.getNodeAs<ParmVarDecl>(
"movable-param");
106 const auto *ConstructorDecl =
107 Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor-decl");
108 const auto *InitArg = Result.Nodes.getNodeAs<DeclRefExpr>(
"init-arg");
110 if (parmVarDeclRefExprOccurences(*MovableParam, *ConstructorDecl,
111 *Result.Context) > 1)
113 auto DiagOut =
diag(InitArg->getLocStart(),
114 "value argument %0 can be moved to avoid copy")
116 DiagOut << FixItHint::CreateReplacement(
117 InitArg->getSourceRange(),
118 (Twine(
"std::move(") + MovableParam->getName() +
")").str());
119 if (
auto IncludeFixit = Inserter->CreateIncludeInsertion(
120 Result.SourceManager->getFileID(InitArg->getLocStart()),
"utility",
122 DiagOut << *IncludeFixit;
126 void MoveConstructorInitCheck::handleMoveConstructor(
127 const MatchFinder::MatchResult &Result) {
128 const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor");
129 const auto *Initializer = Result.Nodes.getNodeAs<CXXCtorInitializer>(
"move-init");
133 QualType QT = Initializer->getInit()->getType();
134 if (QT.isTriviallyCopyableType(*Result.Context))
137 const auto *RD = QT->getAsCXXRecordDecl();
138 if (RD && RD->isTriviallyCopyable())
143 const CXXConstructorDecl *Candidate =
nullptr;
144 for (
const auto *Ctor : CopyCtor->getParent()->ctors()) {
145 if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected &&
146 !Ctor->isDeleted()) {
161 diag(Initializer->getSourceLocation(),
162 "move constructor initializes %0 by calling a copy constructor")
163 << (Initializer->isBaseInitializer() ?
"base class" :
"class member");
164 diag(CopyCtor->getLocation(),
"copy constructor being called",
165 DiagnosticIDs::Note);
166 diag(Candidate->getLocation(),
"candidate move constructor here",
167 DiagnosticIDs::Note);
173 Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
174 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
180 Options.
store(Opts,
"UseCERTSemantics", UseCERTSemantics ? 1 : 0);
LangOptions getLangOpts() const
Returns the language options from the context.
std::unique_ptr< ast_matchers::MatchFinder > Finder
static StringRef toString(IncludeStyle Style)
Converts IncludeStyle to string representation.
Base class for all clang-tidy checks.
void registerPPCallbacks(clang::CompilerInstance &Compiler) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
llvm::Optional< bool > isExpensiveToCopy(QualType Type, const ASTContext &Context)
Returns true if Type is expensive to copy.
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
Produces fixes to insert specified includes to source files, if not yet present.
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.