void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) { const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move"); const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr"); const Expr *Arg = CallMove->getArg(0); SourceManager &SM = Result.Context->getSourceManager(); CharSourceRange MoveRange = CharSourceRange::getCharRange(CallMove->getSourceRange()); CharSourceRange FileMoveRange = Lexer::makeFileCharRange(MoveRange, SM, getLangOpts()); if (!FileMoveRange.isValid()) return; bool IsConstArg = Arg->getType().isConstQualified(); bool IsTriviallyCopyable = Arg->getType().isTriviallyCopyableType(*Result.Context); if (IsConstArg || IsTriviallyCopyable) { if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) { // According to [expr.prim.lambda]p3, "whether the closure type is // trivially copyable" property can be changed by the implementation of // the language, so we shouldn't rely on it when issuing diagnostics. if (R->isLambda()) return; // Don't warn when the type is not copyable. for (const auto *Ctor : R->ctors()) { if (Ctor->isCopyConstructor() && Ctor->isDeleted()) return; } } if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove) return; bool IsVariable = isa<DeclRefExpr>(Arg); const auto *Var = IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr; auto Diag = diag(FileMoveRange.getBegin(), "std::move of the %select{|const }0" "%select{expression|variable %4}1 " "%select{|of the trivially-copyable type %5 }2" "has no effect; remove std::move()" "%select{| or make the variable non-const}3") << IsConstArg << IsVariable << IsTriviallyCopyable << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var << Arg->getType(); ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); } else if (ReceivingExpr) { auto Diag = diag(FileMoveRange.getBegin(), "passing result of std::move() as a const reference " "argument; no move will actually happen"); ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); } }
void MoveConstantArgumentCheck::check(const MatchFinder::MatchResult &Result) { const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move"); const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr"); const Expr *Arg = CallMove->getArg(0); SourceManager &SM = Result.Context->getSourceManager(); CharSourceRange MoveRange = CharSourceRange::getCharRange(CallMove->getSourceRange()); CharSourceRange FileMoveRange = Lexer::makeFileCharRange(MoveRange, SM, getLangOpts()); if (!FileMoveRange.isValid()) return; bool IsConstArg = Arg->getType().isConstQualified(); bool IsTriviallyCopyable = Arg->getType().isTriviallyCopyableType(*Result.Context); if (IsConstArg || IsTriviallyCopyable) { bool IsVariable = isa<DeclRefExpr>(Arg); const auto *Var = IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr; auto Diag = diag(FileMoveRange.getBegin(), "std::move of the %select{|const }0" "%select{expression|variable %4}1 " "%select{|of the trivially-copyable type %5 }2" "has no effect; remove std::move()" "%select{| or make the variable non-const}3") << IsConstArg << IsVariable << IsTriviallyCopyable << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var << Arg->getType(); ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); } else if (ReceivingExpr) { auto Diag = diag(FileMoveRange.getBegin(), "passing result of std::move() as a const reference " "argument; no move will actually happen"); ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); } }