Skip to content

Commit df2de0a

Browse files
[clang-tidy] Improve bugprone.use-after-move interaction with explicit destructor call. (llvm#188866)
It is valid (although niche) to call an explicit destructor after moving the object.
1 parent 199ac48 commit df2de0a

File tree

4 files changed

+54
-4
lines changed

4 files changed

+54
-4
lines changed

clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ class UseAfterMoveFinder {
8282
llvm::SmallPtrSet<const CFGBlock *, 8> Visited;
8383
};
8484

85+
AST_MATCHER_P(Expr, hasParentIgnoringParenImpCasts,
86+
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
87+
const Expr *E = &Node;
88+
do {
89+
const DynTypedNodeList Parents = Finder->getASTContext().getParents(*E);
90+
if (Parents.size() != 1)
91+
return false;
92+
E = Parents[0].get<Expr>();
93+
if (!E)
94+
return false;
95+
} while (isa<ImplicitCastExpr, ParenExpr>(E));
96+
97+
return InnerMatcher.matches(*E, Finder, Builder);
98+
}
99+
85100
} // namespace
86101

87102
static auto getNameMatcher(llvm::ArrayRef<StringRef> InvalidationFunctions) {
@@ -401,9 +416,12 @@ void UseAfterMoveFinder::getDeclRefs(
401416
}
402417
};
403418

404-
auto DeclRefMatcher = declRefExpr(hasDeclaration(equalsNode(MovedVariable)),
405-
unless(inDecltypeOrTemplateArg()))
406-
.bind("declref");
419+
auto DeclRefMatcher =
420+
declRefExpr(hasDeclaration(equalsNode(MovedVariable)),
421+
unless(inDecltypeOrTemplateArg()),
422+
unless(hasParentIgnoringParenImpCasts(
423+
memberExpr(hasDeclaration(cxxDestructorDecl())))))
424+
.bind("declref");
407425

408426
AddDeclRefs(match(traverse(TK_AsIs, findAll(DeclRefMatcher)), *S->getStmt(),
409427
*Context));

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ Changes in existing checks
275275
- Add support for annotation of user-defined types as having the same
276276
moved-from semantics as standard smart pointers.
277277

278+
- Do not report explicit call to destructor after move as an invalid use.
279+
278280
- Improved :doc:`cppcoreguidelines-avoid-capturing-lambda-coroutines
279281
<clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines>`
280282
check by adding the `AllowExplicitObjectParameters` option. When enabled,

clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ Use
193193
---
194194

195195
Any occurrence of the moved variable that is not a reinitialization (see below)
196-
is considered to be a use.
196+
or an explicit call to the variable destructor is considered to be a use.
197197

198198
An exception to this are objects of type ``std::unique_ptr``,
199199
``std::shared_ptr``, ``std::weak_ptr``, ``std::optional``, and ``std::any``,

clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,36 @@ void selfMove() {
8989
a.foo();
9090
}
9191

92+
void * operator new(size_t, void *p);
93+
94+
// Don't flag an explicit destructor call
95+
void explicitDestructor() {
96+
alignas(A) char storage[sizeof(A)];
97+
A& a = *new (storage) A();
98+
std::move(a);
99+
a.~A(); // It's always valid to destruct a moved object.
100+
101+
using B = AnnotatedContainer<int>;
102+
alignas(B) char other_storage[sizeof(B)];
103+
B& a_p = *new (other_storage) B();
104+
std::move(a_p);
105+
a_p.~B(); // Same as above, but with a template class.
106+
107+
A& b = *new (storage) A();
108+
std::move(b);
109+
(b).~A(); // Parenthesis should not change the behavior.
110+
b.foo(); // But destruction is not a reinitialization.
111+
// CHECK-NOTES: [[@LINE-1]]:3: warning: 'b' used after it was moved
112+
// CHECK-NOTES: [[@LINE-4]]:3: note: move occurred here
113+
114+
A& c = *new (storage) A();
115+
std::move(c);
116+
c.foo();
117+
// CHECK-NOTES: [[@LINE-1]]:3: warning: 'c' used after it was moved
118+
// CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here
119+
c.~A();
120+
}
121+
92122
// A warning should only be emitted for one use-after-move.
93123
void onlyFlagOneUseAfterMove() {
94124
A a;

0 commit comments

Comments
 (0)