10#include "clang/AST/ASTContext.h"
11#include "clang/AST/RecursiveASTVisitor.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/Frontend/CompilerInstance.h"
15#include "clang/Lex/Lexer.h"
16#include "clang/Lex/Preprocessor.h"
24 const CXXRecordDecl *Class) {
26 Class->friends(), [Friend](FriendDecl *FriendDecl) ->
bool {
27 if (const TypeSourceInfo *FriendTypeSource =
28 FriendDecl->getFriendType()) {
29 const QualType FriendType = FriendTypeSource->getType();
30 return FriendType->getAsCXXRecordDecl() == Friend;
61AST_MATCHER_P(CXXRecordDecl, isMoveConstructibleInBoundCXXRecordDecl, StringRef,
63 return Builder->removeBindings(
65 &Node](
const ast_matchers::internal::BoundNodesMap &Nodes) ->
bool {
66 const auto *BoundClass =
67 Nodes.getNode(this->RecordDeclID).get<CXXRecordDecl>();
68 for (
const CXXConstructorDecl *Ctor : Node.ctors()) {
69 if (Ctor->isMoveConstructor() && !Ctor->isDeleted() &&
70 (Ctor->getAccess() == AS_public ||
71 (BoundClass && isFirstFriendOfSecond(BoundClass, &Node))))
80 return lValueReferenceType(
81 pointee(unless(templateSpecializationType()), isConstQualified()));
85 return qualType(unless(anyOf(referenceType(), isConstQualified())));
92 const ParmVarDecl *ParamDecl) {
97 class ExactlyOneUsageVisitor
98 :
public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
99 friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;
102 ExactlyOneUsageVisitor(
const ParmVarDecl *ParamDecl)
103 : ParamDecl(ParamDecl) {}
108 bool hasExactlyOneUsageIn(
const CXXConstructorDecl *Ctor) {
110 TraverseDecl(
const_cast<CXXConstructorDecl *
>(Ctor));
118 bool VisitDeclRefExpr(DeclRefExpr *D) {
119 if (
const ParmVarDecl *To = dyn_cast<ParmVarDecl>(D->getDecl())) {
120 if (To == ParamDecl) {
131 const ParmVarDecl *ParamDecl;
135 return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
162 const ParmVarDecl *Param) {
163 if (!Param->getType().getCanonicalType()->isLValueReferenceType()) {
167 const int ParamIdx = Param->getFunctionScopeIndex();
168 const CXXRecordDecl *Record = Ctor->getParent();
172 const auto IsRValueOverload = [&Ctor, ParamIdx](
const CXXConstructorDecl *C) {
173 if (C == Ctor || C->isDeleted() ||
174 C->getNumParams() != Ctor->getNumParams())
176 for (
int I = 0, E = C->getNumParams(); I < E; ++I) {
177 const clang::QualType CandidateParamType =
178 C->parameters()[I]->getType().getCanonicalType();
179 const clang::QualType CtorParamType =
180 Ctor->parameters()[I]->getType().getCanonicalType();
181 const bool IsLValueRValuePair =
182 CtorParamType->isLValueReferenceType() &&
183 CandidateParamType->isRValueReferenceType() &&
184 CandidateParamType->getPointeeType()->getUnqualifiedDesugaredType() ==
185 CtorParamType->getPointeeType()->getUnqualifiedDesugaredType();
188 if (!IsLValueRValuePair)
192 if (!(CandidateParamType == CtorParamType || IsLValueRValuePair))
199 for (
const auto *Candidate : Record->ctors()) {
200 if (IsRValueOverload(Candidate))
208static SmallVector<const ParmVarDecl *, 2>
210 const ParmVarDecl *ParamDecl) {
211 SmallVector<const ParmVarDecl *, 2> Results;
212 const unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
214 for (
const FunctionDecl *Redecl : Ctor->redecls())
215 Results.push_back(Redecl->getParamDecl(ParamIdx));
221 Inserter(Options.getLocalOrGlobal(
"IncludeStyle",
222 utils::IncludeSorter::IS_LLVM),
223 areDiagsSelfContained()),
224 ValuesOnly(Options.get(
"ValuesOnly", false)) {}
227 Options.store(Opts,
"IncludeStyle", Inserter.getStyle());
228 Options.store(Opts,
"ValuesOnly", ValuesOnly);
236 ofClass(cxxRecordDecl().bind(
"outer")),
237 forEachConstructorInitializer(
239 unless(isBaseInitializer()),
245 withInitializer(cxxConstructExpr(
246 has(ignoringParenImpCasts(declRefExpr(to(
258 hasDeclaration(cxxConstructorDecl(
259 isCopyConstructor(), unless(isDeleted()),
260 hasDeclContext(cxxRecordDecl(
261 isMoveConstructibleInBoundCXXRecordDecl(
263 .bind(
"Initializer")))
270 Preprocessor *ModuleExpanderPP) {
271 Inserter.registerPreprocessor(PP);
275 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"Ctor");
276 const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>(
"Param");
277 const auto *Initializer =
278 Result.Nodes.getNodeAs<CXXCtorInitializer>(
"Initializer");
279 const SourceManager &SM = *Result.SourceManager;
288 if (ParamDecl->getType().getNonReferenceType().isTriviallyCopyableType(
296 auto Diag = diag(ParamDecl->getBeginLoc(),
"pass by value and use std::move");
300 if (ParamDecl->getType()->isLValueReferenceType()) {
303 const TypeLoc ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
304 auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
305 if (RefTL.isNull()) {
313 const TypeLoc ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
314 auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
316 const TypeLoc ValueTL = RefTL.getPointeeLoc();
317 const CharSourceRange TypeRange = CharSourceRange::getTokenRange(
318 ParmDecl->getBeginLoc(), ParamTL.getEndLoc());
319 std::string ValueStr =
320 Lexer::getSourceText(
321 CharSourceRange::getTokenRange(ValueTL.getSourceRange()), SM,
325 Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
330 Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(),
")")
331 << FixItHint::CreateInsertion(
332 Initializer->getLParenLoc().getLocWithOffset(1),
"std::move(")
333 << Inserter.createIncludeInsertion(
334 Result.SourceManager->getFileID(Initializer->getSourceLocation()),
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
PassByValueCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
static SmallVector< const ParmVarDecl *, 2 > collectParamDecls(const CXXConstructorDecl *Ctor, const ParmVarDecl *ParamDecl)
Find all references to ParamDecl across all of the redeclarations of Ctor.
static bool hasRValueOverload(const CXXConstructorDecl *Ctor, const ParmVarDecl *Param)
Returns true if the given constructor is part of a lvalue/rvalue reference pair, i....
static bool isFirstFriendOfSecond(const CXXRecordDecl *Friend, const CXXRecordDecl *Class)
static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor, const ParmVarDecl *ParamDecl)
Whether or not ParamDecl is used exactly one time in Ctor.
static TypeMatcher nonConstValueType()
static TypeMatcher notTemplateSpecConstRefType()
Some operations such as code completion produce a set of candidates.
llvm::StringMap< ClangTidyValue > OptionMap