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);
42 auto MoveCallMatcher =
43 callExpr(callee(functionDecl(hasName(
"::std::move"))), argumentCountIs(1),
44 unless(isInTemplateInstantiation()))
48 auto TernaryWithMoveMatcher =
49 conditionalOperator(hasDescendant(MoveCallMatcher));
53 castExpr(hasSourceExpression(MoveCallMatcher)),
54 cxxConstructExpr(hasDeclaration(cxxConstructorDecl(anyOf(
55 isCopyConstructor(), isMoveConstructor()))),
56 hasArgument(0, MoveCallMatcher)))),
59 auto ConstTypeParmMatcher =
60 qualType(references(isConstQualified())).bind(
"invocation-parm-type");
61 auto RValueTypeParmMatcher =
62 qualType(rValueReferenceType()).bind(
"invocation-parm-type");
64 auto ArgumentWithParamMatcher = forEachArgumentWithParam(
65 anyOf(MoveCallMatcher, TernaryWithMoveMatcher),
67 anyOf(hasType(ConstTypeParmMatcher), hasType(RValueTypeParmMatcher)))
68 .bind(
"invocation-parm"));
71 auto ArgumentWithParamTypeMatcher = forEachArgumentWithParamType(
72 anyOf(MoveCallMatcher, TernaryWithMoveMatcher),
73 anyOf(ConstTypeParmMatcher, RValueTypeParmMatcher));
76 invocation(anyOf(ArgumentWithParamMatcher, ArgumentWithParamTypeMatcher))
77 .bind(
"receiving-expr"),
101 const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>(
"call-move");
102 const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>(
"receiving-expr");
103 const auto *InvocationParm =
104 Result.Nodes.getNodeAs<ParmVarDecl>(
"invocation-parm");
105 const auto *InvocationParmType =
106 Result.Nodes.getNodeAs<QualType>(
"invocation-parm-type");
109 if (!ReceivingExpr && AlreadyCheckedMoves.contains(CallMove))
113 AlreadyCheckedMoves.insert(CallMove);
115 const Expr *Arg = CallMove->getArg(0);
116 const QualType ArgType = Arg->getType().getCanonicalType();
117 SourceManager &SM = Result.Context->getSourceManager();
119 CharSourceRange MoveRange =
120 CharSourceRange::getCharRange(CallMove->getSourceRange());
121 CharSourceRange FileMoveRange =
122 Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
123 if (!FileMoveRange.isValid())
126 bool IsConstArg = ArgType.isConstQualified();
127 bool IsTriviallyCopyable = ArgType.isTriviallyCopyableType(*Result.Context);
129 if (IsConstArg || IsTriviallyCopyable) {
130 if (
const CXXRecordDecl *R = ArgType->getAsCXXRecordDecl()) {
137 for (
const auto *Ctor : R->ctors()) {
138 if (Ctor->isCopyConstructor() && Ctor->isDeleted())
143 if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)
146 bool IsVariable = isa<DeclRefExpr>(Arg);
152 IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() :
nullptr;
155 auto Diag = diag(FileMoveRange.getBegin(),
156 "std::move of the %select{|const }0"
157 "%select{expression|variable %5}1 "
158 "%select{|of the trivially-copyable type %6 }2"
159 "has no effect%select{; remove std::move()|}3"
160 "%select{| or make the variable non-const}4")
161 << IsConstArg << IsVariable << IsTriviallyCopyable
163 << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
170 const auto *ReceivingCallExpr = dyn_cast<CallExpr>(ReceivingExpr);
171 const auto *ReceivingConstructExpr =
172 dyn_cast<CXXConstructExpr>(ReceivingExpr);
174 if ((!ReceivingCallExpr || !ReceivingCallExpr->getDirectCallee() ||
175 ReceivingCallExpr->getDirectCallee()->isTemplateInstantiation()) &&
176 (!ReceivingConstructExpr ||
177 !ReceivingConstructExpr->getConstructor() ||
178 ReceivingConstructExpr->getConstructor()->isTemplateInstantiation()))
181 const NamedDecl *FunctionName =
nullptr;
184 ? ReceivingCallExpr->getDirectCallee()->getUnderlyingDecl()
185 : ReceivingConstructExpr->getConstructor()->getUnderlyingDecl();
187 QualType NoRefType = (*InvocationParmType)->getPointeeType();
188 PrintingPolicy PolicyWithSuppressedTag(getLangOpts());
189 PolicyWithSuppressedTag.SuppressTagKeyword =
true;
190 PolicyWithSuppressedTag.SuppressUnwrittenScope =
true;
191 std::string ExpectParmTypeName =
192 NoRefType.getAsString(PolicyWithSuppressedTag);
193 if (!NoRefType->isPointerType()) {
194 NoRefType.addConst();
196 NoRefType.getAsString(PolicyWithSuppressedTag) +
" &";
199 diag(InvocationParm->getLocation(),
200 "consider changing the %ordinal0 parameter of %1 from %2 to '%3'",
202 << (InvocationParm->getFunctionScopeIndex() + 1) << FunctionName
203 << *InvocationParmType << ExpectParmTypeName;
205 }
else if (ReceivingExpr && CheckMoveToConstRef) {
206 if ((*InvocationParmType)->isRValueReferenceType())
210 auto Diag = diag(FileMoveRange.getBegin(),
211 "passing result of std::move() as a const reference "
212 "argument; no move will actually happen");
217 if (
const CXXRecordDecl *RecordDecl = ArgType->getAsCXXRecordDecl();
218 RecordDecl && RecordDecl->hasDefinition() &&
219 !(RecordDecl->hasMoveConstructor() &&
220 RecordDecl->hasMoveAssignment())) {
221 const bool MissingMoveAssignment = !RecordDecl->hasMoveAssignment();
222 const bool MissingMoveConstructor = !RecordDecl->hasMoveConstructor();
223 const bool MissingBoth = MissingMoveAssignment && MissingMoveConstructor;
225 diag(RecordDecl->getLocation(),
227 "%select{|assignable}1%select{|/}2%select{|constructible}3",
229 << RecordDecl << MissingMoveAssignment << MissingBoth
230 << MissingMoveConstructor;