11#include "clang/Lex/Lexer.h"
18 const SourceManager &SM,
19 const LangOptions &LangOpts) {
20 const Expr *Arg = Call->getArg(0);
22 CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange(
23 CharSourceRange::getCharRange(Call->getBeginLoc(), Arg->getBeginLoc()),
25 CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(
26 CharSourceRange::getCharRange(Call->getEndLoc(),
27 Call->getEndLoc().getLocWithOffset(1)),
30 if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {
31 Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)
32 << FixItHint::CreateRemoval(AfterArgumentsRange);
37 Options.
store(Opts,
"CheckTriviallyCopyableMove", CheckTriviallyCopyableMove);
38 Options.
store(Opts,
"CheckMoveToConstRef", CheckMoveToConstRef);
42 auto MoveCallMatcher =
43 callExpr(callee(functionDecl(hasName(
"::std::move"))), argumentCountIs(1),
44 unless(isInTemplateInstantiation()))
49 castExpr(hasSourceExpression(MoveCallMatcher)),
50 cxxConstructExpr(hasDeclaration(cxxConstructorDecl(anyOf(
51 isCopyConstructor(), isMoveConstructor()))),
52 hasArgument(0, MoveCallMatcher)))),
55 auto ConstTypeParmMatcher =
56 qualType(references(isConstQualified())).bind(
"invocation-parm-type");
57 auto RValueTypeParmMatcher =
58 qualType(rValueReferenceType()).bind(
"invocation-parm-type");
60 auto ArgumentWithParamMatcher = forEachArgumentWithParam(
61 MoveCallMatcher, parmVarDecl(anyOf(hasType(ConstTypeParmMatcher),
62 hasType(RValueTypeParmMatcher)))
63 .bind(
"invocation-parm"));
66 auto ArgumentWithParamTypeMatcher = forEachArgumentWithParamType(
67 MoveCallMatcher, anyOf(ConstTypeParmMatcher, RValueTypeParmMatcher));
70 invocation(anyOf(ArgumentWithParamMatcher, ArgumentWithParamTypeMatcher))
71 .bind(
"receiving-expr"),
76 const QualType *InvocationParmType,
78 if (Invocation && (*InvocationParmType)->isRValueReferenceType() &&
80 if (!Invocation->getType()->isRecordType())
82 if (
const auto *ConstructCallExpr =
83 dyn_cast<CXXConstructExpr>(Invocation)) {
84 if (
const auto *ConstructorDecl = ConstructCallExpr->getConstructor()) {
85 if (!ConstructorDecl->isCopyOrMoveConstructor() &&
86 !ConstructorDecl->isDefaultConstructor())
95 const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>(
"call-move");
96 const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>(
"receiving-expr");
97 const auto *InvocationParm =
98 Result.Nodes.getNodeAs<ParmVarDecl>(
"invocation-parm");
99 const auto *InvocationParmType =
100 Result.Nodes.getNodeAs<QualType>(
"invocation-parm-type");
103 if (!ReceivingExpr && AlreadyCheckedMoves.contains(CallMove))
107 AlreadyCheckedMoves.insert(CallMove);
109 const Expr *Arg = CallMove->getArg(0);
110 const QualType ArgType = Arg->getType().getCanonicalType();
111 SourceManager &SM = Result.Context->getSourceManager();
113 CharSourceRange MoveRange =
114 CharSourceRange::getCharRange(CallMove->getSourceRange());
115 CharSourceRange FileMoveRange =
116 Lexer::makeFileCharRange(MoveRange, SM,
getLangOpts());
117 if (!FileMoveRange.isValid())
120 bool IsConstArg = ArgType.isConstQualified();
121 bool IsTriviallyCopyable = ArgType.isTriviallyCopyableType(*Result.Context);
123 if (IsConstArg || IsTriviallyCopyable) {
124 if (
const CXXRecordDecl *R = ArgType->getAsCXXRecordDecl()) {
131 for (
const auto *Ctor : R->ctors()) {
132 if (Ctor->isCopyConstructor() && Ctor->isDeleted())
137 if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)
140 bool IsVariable = isa<DeclRefExpr>(Arg);
146 IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() :
nullptr;
149 auto Diag =
diag(FileMoveRange.getBegin(),
150 "std::move of the %select{|const }0"
151 "%select{expression|variable %5}1 "
152 "%select{|of the trivially-copyable type %6 }2"
153 "has no effect%select{; remove std::move()|}3"
154 "%select{| or make the variable non-const}4")
155 << IsConstArg << IsVariable << IsTriviallyCopyable
157 << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
164 const auto *ReceivingCallExpr = dyn_cast<CallExpr>(ReceivingExpr);
165 const auto *ReceivingConstructExpr =
166 dyn_cast<CXXConstructExpr>(ReceivingExpr);
168 if ((!ReceivingCallExpr || !ReceivingCallExpr->getDirectCallee() ||
169 ReceivingCallExpr->getDirectCallee()->isTemplateInstantiation()) &&
170 (!ReceivingConstructExpr ||
171 !ReceivingConstructExpr->getConstructor() ||
172 ReceivingConstructExpr->getConstructor()->isTemplateInstantiation()))
175 const NamedDecl *FunctionName =
nullptr;
178 ? ReceivingCallExpr->getDirectCallee()->getUnderlyingDecl()
179 : ReceivingConstructExpr->getConstructor()->getUnderlyingDecl();
181 QualType NoRefType = (*InvocationParmType)->getPointeeType();
182 PrintingPolicy PolicyWithSuppressedTag(
getLangOpts());
183 PolicyWithSuppressedTag.SuppressTagKeyword =
true;
184 PolicyWithSuppressedTag.SuppressUnwrittenScope =
true;
185 std::string ExpectParmTypeName =
186 NoRefType.getAsString(PolicyWithSuppressedTag);
187 if (!NoRefType->isPointerType()) {
188 NoRefType.addConst();
190 NoRefType.getAsString(PolicyWithSuppressedTag) +
" &";
193 diag(InvocationParm->getLocation(),
194 "consider changing the %ordinal0 parameter of %1 from %2 to '%3'",
196 << (InvocationParm->getFunctionScopeIndex() + 1) << FunctionName
197 << *InvocationParmType << ExpectParmTypeName;
199 }
else if (ReceivingExpr && CheckMoveToConstRef) {
200 if ((*InvocationParmType)->isRValueReferenceType())
204 auto Diag =
diag(FileMoveRange.getBegin(),
205 "passing result of std::move() as a const reference "
206 "argument; no move will actually happen");
211 if (
const CXXRecordDecl *RecordDecl = ArgType->getAsCXXRecordDecl();
212 RecordDecl && RecordDecl->hasDefinition() &&
213 !(RecordDecl->hasMoveConstructor() &&
214 RecordDecl->hasMoveAssignment())) {
215 const bool MissingMoveAssignment = !RecordDecl->hasMoveAssignment();
216 const bool MissingMoveConstructor = !RecordDecl->hasMoveConstructor();
217 const bool MissingBoth = MissingMoveAssignment && MissingMoveConstructor;
219 diag(RecordDecl->getLocation(),
221 "%select{|assignable}1%select{|/}2%select{|constructible}3",
223 << RecordDecl << MissingMoveAssignment << MissingBoth
224 << MissingMoveConstructor;
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.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
llvm::StringMap< ClangTidyValue > OptionMap