Index: clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.h =================================================================== --- clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.h +++ clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTANTARGUMENTCHECK_H #include "../ClangTidyCheck.h" +#include "llvm/ADT/DenseSet.h" namespace clang { namespace tidy { @@ -36,6 +37,7 @@ private: const bool CheckTriviallyCopyableMove; + llvm::DenseSet HasCheckedMoveSet; }; } // namespace performance Index: clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp +++ clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp @@ -50,7 +50,9 @@ Finder->addMatcher( invocation(forEachArgumentWithParam( MoveCallMatcher, - parmVarDecl(hasType(references(isConstQualified()))))) + parmVarDecl(anyOf(hasType(references(isConstQualified())), + hasType(rValueReferenceType()))) + .bind("invocation-parm"))) .bind("receiving-expr"), this); } @@ -58,6 +60,15 @@ void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) { const auto *CallMove = Result.Nodes.getNodeAs("call-move"); const auto *ReceivingExpr = Result.Nodes.getNodeAs("receiving-expr"); + const auto *InvocationParm = + Result.Nodes.getNodeAs("invocation-parm"); + + if (!ReceivingExpr && HasCheckedMoveSet.contains(CallMove)) + return; + + if (ReceivingExpr) + HasCheckedMoveSet.insert(CallMove); + const Expr *Arg = CallMove->getArg(0); SourceManager &SM = Result.Context->getSourceManager(); @@ -90,20 +101,52 @@ return; bool IsVariable = isa(Arg); + bool IsRValueReferenceArg = false; const auto *Var = IsVariable ? dyn_cast(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()); + if (ReceivingExpr && + InvocationParm->getOriginalType()->isRValueReferenceType() && + !ReceivingExpr->getType()->isRecordType() && Arg->isLValue()) { + IsRValueReferenceArg = true; + } + + { + auto Diag = diag(FileMoveRange.getBegin(), + "std::move of the %select{|const }0" + "%select{expression|variable %5}1 " + "%select{|of the trivially-copyable type %6 }2" + "has no effect%select{; remove std::move()|}3" + "%select{| or make the variable non-const}4") + << IsConstArg << IsVariable << IsTriviallyCopyable + << IsRValueReferenceArg + << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var + << Arg->getType(); + if (!IsRValueReferenceArg) + replaceCallWithArg(CallMove, Diag, SM, getLangOpts()); + } + if (IsRValueReferenceArg) { + const auto *ReceivingCallExpr = dyn_cast(ReceivingExpr); + if (!ReceivingCallExpr) + return; + std::string ExpectParmTypeName; + if (Arg->getType()->isRecordType()) { + if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) + ExpectParmTypeName = R->getNameAsString(); + } else + ExpectParmTypeName = + Arg->getType().getAtomicUnqualifiedType().getAsString(); + diag(InvocationParm->getLocation(), + "consider changing the %0st parameter of %1 from %2 to 'const %3 &'", + DiagnosticIDs::Note) + << (InvocationParm->getFunctionScopeIndex() + 1) + << ReceivingCallExpr->getDirectCallee()->getName() + << InvocationParm->getOriginalType() << ExpectParmTypeName; + } } else if (ReceivingExpr) { + if (InvocationParm->getOriginalType()->isRValueReferenceType()) + return; + auto Diag = diag(FileMoveRange.getBegin(), "passing result of std::move() as a const reference " "argument; no move will actually happen"); Index: clang-tools-extra/test/clang-tidy/checkers/performance-move-const-arg.cpp =================================================================== --- clang-tools-extra/test/clang-tidy/checkers/performance-move-const-arg.cpp +++ clang-tools-extra/test/clang-tidy/checkers/performance-move-const-arg.cpp @@ -246,3 +246,40 @@ }; f(MoveSemantics()); } + +void showInt(int &&v) {} +void showInt(int v1, int &&v2) {} +void testInt() { + int a = 10; + showInt(std::move(a)); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg] + // CHECK-MESSAGES: :[[@LINE-6]]:20: note: consider changing the 1st parameter of showInt from 'int &&' to 'const int &' + showInt(int()); + showInt(a, std::move(a)); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg] + // CHECK-MESSAGES: :[[@LINE-9]]:28: note: consider changing the 2st parameter of showInt from 'int &&' to 'const int &' +} +template +void forwardToShowInt(T && t) { + showInt(static_cast(t)); +} +void testTemplate() { + int a = 10; + forwardToShowInt(std::move(a)); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: std::move of the variable 'a' of the trivially-copyable type 'int' has no effect [performance-move-const-arg] +} + +struct Tmp {}; +void showTmp(Tmp &&v) {} +void testTmp() { + Tmp t; + showTmp(std::move(t)); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: std::move of the variable 't' of the trivially-copyable type 'Tmp' has no effect [performance-move-const-arg] + // CHECK-MESSAGES: :[[@LINE-5]]:20: note: consider changing the 1st parameter of showTmp from 'Tmp &&' to 'const Tmp &' +} + +void showA(A && v) {} +void testA() { + A a; + showA(std::move(a)); +}