summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang-tools-extra/clang-tidy/misc/UseAfterMoveCheck.cpp9
-rw-r--r--clang-tools-extra/test/clang-tidy/misc-use-after-move.cpp17
2 files changed, 24 insertions, 2 deletions
diff --git a/clang-tools-extra/clang-tidy/misc/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/misc/UseAfterMoveCheck.cpp
index 471b7506375..842aa5b3f72 100644
--- a/clang-tools-extra/clang-tidy/misc/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/UseAfterMoveCheck.cpp
@@ -384,6 +384,13 @@ void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) {
// the direct ancestor of the std::move() that isn't one of the node
// types ignored by ignoringParenImpCasts().
stmt(forEach(expr(ignoringParenImpCasts(CallMoveMatcher))),
+ // Don't allow an InitListExpr to be the moving call. An InitListExpr
+ // has both a syntactic and a semantic form, and the parent-child
+ // relationships are different between the two. This could cause an
+ // InitListExpr to be analyzed as the moving call in addition to the
+ // Expr that we actually want, resulting in two diagnostics with
+ // different code locations for the same move.
+ unless(initListExpr()),
unless(expr(ignoringParenImpCasts(equalsBoundNode("call-move")))))
.bind("moving-call"),
this);
@@ -398,7 +405,7 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MovingCall = Result.Nodes.getNodeAs<Expr>("moving-call");
const auto *Arg = Result.Nodes.getNodeAs<DeclRefExpr>("arg");
- if (!MovingCall)
+ if (!MovingCall || !MovingCall->getExprLoc().isValid())
MovingCall = CallMove;
Stmt *FunctionBody = nullptr;
diff --git a/clang-tools-extra/test/clang-tidy/misc-use-after-move.cpp b/clang-tools-extra/test/clang-tidy/misc-use-after-move.cpp
index 92eb1239496..aac901c7690 100644
--- a/clang-tools-extra/test/clang-tidy/misc-use-after-move.cpp
+++ b/clang-tools-extra/test/clang-tidy/misc-use-after-move.cpp
@@ -282,7 +282,7 @@ void moveInInitList() {
S s{std::move(a)};
a.foo();
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
- // CHECK-MESSAGES: [[@LINE-3]]:6: note: move occurred here
+ // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here
}
void lambdas() {
@@ -397,6 +397,21 @@ void movedTypeIsDependentType() {
}
template void movedTypeIsDependentType<A>();
+// We handle the case correctly where the move consists of an implicit call
+// to a conversion operator.
+void implicitConversionOperator() {
+ struct Convertible {
+ operator A() && { return A(); }
+ };
+ void takeA(A a);
+
+ Convertible convertible;
+ takeA(std::move(convertible));
+ convertible;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:9: note: move occurred here
+}
+
// Using decltype on an expression is not a use.
void decltypeIsNotUse() {
A a;
OpenPOWER on IntegriCloud