43 auto MoveCallMatcher =
44 callExpr(callee(functionDecl(hasName(
"::std::move"))), argumentCountIs(1),
45 unless(isInTemplateInstantiation()))
49 auto TernaryWithMoveMatcher =
50 conditionalOperator(hasDescendant(MoveCallMatcher));
54 castExpr(hasSourceExpression(MoveCallMatcher)),
55 cxxConstructExpr(hasDeclaration(cxxConstructorDecl(anyOf(
56 isCopyConstructor(), isMoveConstructor()))),
57 hasArgument(0, MoveCallMatcher)))),
60 auto ConstTypeParmMatcher =
61 qualType(references(isConstQualified())).bind(
"invocation-parm-type");
62 auto RValueTypeParmMatcher =
63 qualType(rValueReferenceType()).bind(
"invocation-parm-type");
65 auto ArgumentWithParamMatcher = forEachArgumentWithParam(
66 anyOf(MoveCallMatcher, TernaryWithMoveMatcher),
68 anyOf(hasType(ConstTypeParmMatcher), hasType(RValueTypeParmMatcher)))
69 .bind(
"invocation-parm"));
72 auto ArgumentWithParamTypeMatcher = forEachArgumentWithParamType(
73 anyOf(MoveCallMatcher, TernaryWithMoveMatcher),
74 anyOf(ConstTypeParmMatcher, RValueTypeParmMatcher));
77 invocation(anyOf(ArgumentWithParamMatcher, ArgumentWithParamTypeMatcher))
78 .bind(
"receiving-expr"),
102 const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>(
"call-move");
103 const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>(
"receiving-expr");
104 const auto *InvocationParm =
105 Result.Nodes.getNodeAs<ParmVarDecl>(
"invocation-parm");
106 const auto *InvocationParmType =
107 Result.Nodes.getNodeAs<QualType>(
"invocation-parm-type");
110 if (!ReceivingExpr && AlreadyCheckedMoves.contains(CallMove))
114 AlreadyCheckedMoves.insert(CallMove);
116 const Expr *Arg = CallMove->getArg(0);
117 const QualType ArgType = Arg->getType().getCanonicalType();
118 const SourceManager &SM = Result.Context->getSourceManager();
120 const CharSourceRange MoveRange =
121 CharSourceRange::getCharRange(CallMove->getSourceRange());
122 const CharSourceRange FileMoveRange =
123 Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
124 if (!FileMoveRange.isValid())
127 const bool IsConstArg = ArgType.isConstQualified();
128 const bool IsTriviallyCopyable =
129 ArgType.isTriviallyCopyableType(*Result.Context);
131 if (IsConstArg || IsTriviallyCopyable) {
132 if (
const CXXRecordDecl *R = ArgType->getAsCXXRecordDecl()) {
139 for (
const auto *Ctor : R->ctors())
140 if (Ctor->isCopyConstructor() &&
141 (Ctor->isDeleted() || Ctor->getAccess() != AS_public))
145 if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)
148 const bool IsVariable = isa<DeclRefExpr>(Arg);
151 const bool IsRVRefParam =
154 IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() :
nullptr;
157 auto Diag = diag(FileMoveRange.getBegin(),
158 "std::move of the %select{|const }0"
159 "%select{expression|variable %5}1 "
160 "%select{|of the trivially-copyable type %6 }2"
161 "has no effect%select{; remove std::move()|}3"
162 "%select{| or make the variable non-const}4")
163 << IsConstArg << IsVariable << IsTriviallyCopyable
165 << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
172 const auto *ReceivingCallExpr = dyn_cast<CallExpr>(ReceivingExpr);
173 const auto *ReceivingConstructExpr =
174 dyn_cast<CXXConstructExpr>(ReceivingExpr);
176 if ((!ReceivingCallExpr || !ReceivingCallExpr->getDirectCallee() ||
177 ReceivingCallExpr->getDirectCallee()->isTemplateInstantiation()) &&
178 (!ReceivingConstructExpr ||
179 !ReceivingConstructExpr->getConstructor() ||
180 ReceivingConstructExpr->getConstructor()->isTemplateInstantiation()))
183 const NamedDecl *FunctionName =
nullptr;
186 ? ReceivingCallExpr->getDirectCallee()->getUnderlyingDecl()
187 : ReceivingConstructExpr->getConstructor()->getUnderlyingDecl();
189 QualType NoRefType = (*InvocationParmType)->getPointeeType();
190 PrintingPolicy PolicyWithSuppressedTag(getLangOpts());
191 PolicyWithSuppressedTag.SuppressTagKeyword =
true;
192 PolicyWithSuppressedTag.SuppressUnwrittenScope =
true;
193 std::string ExpectParmTypeName =
194 NoRefType.getAsString(PolicyWithSuppressedTag);
195 if (!NoRefType->isPointerType()) {
196 NoRefType.addConst();
198 NoRefType.getAsString(PolicyWithSuppressedTag) +
" &";
201 diag(InvocationParm->getLocation(),
202 "consider changing the %ordinal0 parameter of %1 from %2 to '%3'",
204 << (InvocationParm->getFunctionScopeIndex() + 1) << FunctionName
205 << *InvocationParmType << ExpectParmTypeName;
207 }
else if (ReceivingExpr && CheckMoveToConstRef) {
208 if ((*InvocationParmType)->isRValueReferenceType())
212 auto Diag = diag(FileMoveRange.getBegin(),
213 "passing result of std::move() as a const reference "
214 "argument; no move will actually happen");
219 if (
const CXXRecordDecl *RecordDecl = ArgType->getAsCXXRecordDecl();
220 RecordDecl && RecordDecl->hasDefinition() &&
221 !(RecordDecl->hasMoveConstructor() &&
222 RecordDecl->hasMoveAssignment())) {
223 const bool MissingMoveAssignment = !RecordDecl->hasMoveAssignment();
224 const bool MissingMoveConstructor = !RecordDecl->hasMoveConstructor();
225 const bool MissingBoth = MissingMoveAssignment && MissingMoveConstructor;
227 diag(RecordDecl->getLocation(),
229 "%select{|assignable}1%select{|/}2%select{|constructible}3",
231 << RecordDecl << MissingMoveAssignment << MissingBoth
232 << MissingMoveConstructor;