Index: clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp +++ clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp @@ -375,14 +375,38 @@ } static void emitDiagnostic(const Expr *MovingCall, const DeclRefExpr *MoveArg, - const UseAfterMove &Use, ClangTidyCheck *Check, + const UseAfterMove &Use, + const LambdaExpr *Lambda, ClangTidyCheck *Check, ASTContext *Context) { SourceLocation UseLoc = Use.DeclRef->getExprLoc(); SourceLocation MoveLoc = MovingCall->getExprLoc(); + const bool MoveArgIsConst = MoveArg->getType().isConstQualified(); - Check->diag(UseLoc, "'%0' used after it was moved") - << MoveArg->getDecl()->getName(); + Check->diag(UseLoc, "'%0' used after it was moved%select{|; move of a 'const' argument has no effect}1") + << MoveArg->getDecl()->getName() + << (MoveArgIsConst ? 1 : 0); Check->diag(MoveLoc, "move occurred here", DiagnosticIDs::Note); + if (MoveArgIsConst) { + if (Lambda) { + for (auto Capture = Lambda->capture_begin(), End = Lambda->capture_end(); + Capture != End; ++Capture) { + if (MoveArg->getDecl() == Capture->getCapturedVar()) { + const bool IsExplicitCapture = Capture->isExplicit(); + Check->diag( + IsExplicitCapture ? Capture->getLocation() + : Lambda->getCaptureDefaultLoc(), + "variable %0 %select{|implicitly}1 captured const here", + DiagnosticIDs::Note) + << Capture->getCapturedVar() << (IsExplicitCapture ? 0 : 1); + break; + } + } + } else { + Check->diag(MoveArg->getDecl()->getLocation(), + "variable '%0' declared const here", DiagnosticIDs::Note); + } + } + if (Use.EvaluationOrderUndefined) { Check->diag(UseLoc, "the use and move are unsequenced, i.e. there is no guarantee " @@ -452,7 +476,7 @@ UseAfterMoveFinder finder(Result.Context); UseAfterMove Use; if (finder.find(FunctionBody, MovingCall, Arg->getDecl(), &Use)) - emitDiagnostic(MovingCall, Arg, Use, this, Result.Context); + emitDiagnostic(MovingCall, Arg, Use, ContainingLambda, this, Result.Context); } } // namespace bugprone Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-use-after-move.cpp =================================================================== --- clang-tools-extra/test/clang-tidy/checkers/bugprone-use-after-move.cpp +++ clang-tools-extra/test/clang-tidy/checkers/bugprone-use-after-move.cpp @@ -129,6 +129,16 @@ // CHECK-NOTES: [[@LINE-3]]:15: note: move occurred here } +void simpleConst() { + const A a; + a.foo(); + A other_a = std::move(a); + a.foo(); + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved; move of a 'const' argument has no effect + // CHECK-NOTES: [[@LINE-3]]:15: note: move occurred here + // CHECK-NOTES: [[@LINE-6]]:11: note: variable 'a' declared const here +} + // A warning should only be emitted for one use-after-move. void onlyFlagOneUseAfterMove() { A a; @@ -314,8 +324,21 @@ auto lambda = [a] { std::move(a); a.foo(); - // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved; move of a 'const' argument has no effect // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-5]]:20: note: variable 'a' captured const here + }; + } + // Use-after-moves inside a lambda should be detected. + { + A a; + // Implicit capture + auto lambda = [=] { + std::move(a); + a.foo(); + // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved; move of a 'const' argument has no effect + // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-5]]:20: note: variable 'a' implicitly captured const here }; } // This is just as true if the variable was declared inside the lambda. @@ -721,14 +744,16 @@ const A a; std::move(a); passByConstPointer(&a); - // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved; move of a 'const' argument has no effect // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-5]]:13: note: variable 'a' declared const here } const A a; std::move(a); passByConstReference(a); - // CHECK-NOTES: [[@LINE-1]]:24: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-1]]:24: warning: 'a' used after it was moved; move of a 'const' argument has no effect // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here + // CHECK-NOTES: [[@LINE-5]]:11: note: variable 'a' declared const here } // Clearing a standard container using clear() is treated as a