diff --git a/clang-tools-extra/clang-tidy/utils/ExprSequence.cpp b/clang-tools-extra/clang-tidy/utils/ExprSequence.cpp --- a/clang-tools-extra/clang-tidy/utils/ExprSequence.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExprSequence.cpp @@ -95,9 +95,29 @@ return true; } + SmallVector BeforeParents = getParentStmts(Before, Context); + + // Since C++17, the callee of a call expression is guaranteed to be sequenced + // before all of the arguments. + // We handle this as a special case rather than using the general + // `getSequenceSuccessor` logic above because the callee expression doesn't + // have an unambiguous successor; the order in which arguments are evaluated + // is indeterminate. + if (Context->getLangOpts().CPlusPlus17) { + for (const Stmt *Parent : BeforeParents) { + if (const auto *call = dyn_cast(Parent); + call && call->getCallee() == Before) { + for (const Expr *arg : call->arguments()) { + if (isDescendantOrEqual(After, arg, Context)) + return true; + } + } + } + } + // If 'After' is a parent of 'Before' or is sequenced after one of these // parents, we know that it is sequenced after 'Before'. - for (const Stmt *Parent : getParentStmts(Before, Context)) { + for (const Stmt *Parent : BeforeParents) { if (Parent == After || inSequence(Parent, After)) return true; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp @@ -1,3 +1,4 @@ +// RUN: %check_clang_tidy -std=c++11 -check-suffixes=,CXX11 %s bugprone-use-after-move %t -- -- -fno-delayed-template-parsing // RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-use-after-move %t -- -- -fno-delayed-template-parsing typedef decltype(nullptr) nullptr_t; @@ -123,6 +124,7 @@ A &operator=(A &&); void foo() const; + void bar(int i) const; int getInt() const; operator bool() const; @@ -1307,6 +1309,30 @@ } } +// In a function call, the expression that determines the callee is sequenced +// before the arguments -- but only in C++17 and later. +namespace CalleeSequencedBeforeArguments { +int consumeA(std::unique_ptr a); + +void calleeSequencedBeforeArguments() { + { + std::unique_ptr a; + a->bar(consumeA(std::move(a))); + // CHECK-NOTES-CXX11: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-NOTES-CXX11: [[@LINE-2]]:21: note: move occurred here + // CHECK-NOTES-CXX11: [[@LINE-3]]:5: note: the use and move are unsequenced + } + { + std::unique_ptr a; + std::unique_ptr getArg(std::unique_ptr a); + getArg(std::move(a))->bar(a->getInt()); + // CHECK-NOTES: [[@LINE-1]]:31: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:12: note: move occurred here + // CHECK-NOTES-CXX11: [[@LINE-3]]:31: note: the use and move are unsequenced + } +} +} // namespace CalleeSequencedBeforeArguments + // Some statements in templates (e.g. null, break and continue statements) may // be shared between the uninstantiated and instantiated versions of the // template and therefore have multiple parents. Make sure the sequencing code