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 @@ -373,14 +373,36 @@ } 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(); + int 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; Check->diag(MoveLoc, "move occurred here", DiagnosticIDs::Note); + if (MoveArgIsConst) { + if (Lambda) { + for (const auto &Capture : Lambda->captures()) { + if (MoveArg->getDecl() == Capture.getCapturedVar()) { + int IsExplicitCapture = Capture.isExplicit(); + Check->diag(IsExplicitCapture ? Capture.getLocation() + : Lambda->getCaptureDefaultLoc(), + "variable %0 %select{implicitly|}1 captured const here", + DiagnosticIDs::Note) + << Capture.getCapturedVar() << IsExplicitCapture; + 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 " @@ -447,7 +469,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