diff --git a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -618,10 +618,6 @@ if (!IC) return; - // Calling a destructor on a moved object is fine. - if (isa(IC)) - return; - const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); if (!ThisRegion) return; @@ -631,6 +627,10 @@ if (!MethodDecl) return; + // Calling a destructor on a moved object is fine. + if (isa(MethodDecl)) + return; + // We want to investigate the whole object, not only sub-object of a parent // class in which the encountered method defined. ThisRegion = ThisRegion->getMostDerivedObjectRegion(); diff --git a/clang/test/Analysis/use-after-move.cpp b/clang/test/Analysis/use-after-move.cpp --- a/clang/test/Analysis/use-after-move.cpp +++ b/clang/test/Analysis/use-after-move.cpp @@ -900,6 +900,28 @@ } } +void checkExplicitDestructorCalls() { + // The below code segments invoke the destructor twice (explicit and + // implicit). While this is not a desired code behavior, it is + // not the use-after-move checker's responsibility to issue such a warning. + { + B* b = new B; + B a = std::move(*b); + b->~B(); // no-warning + delete b; + } + { + B a, b; + new (&a) B(reinterpret_cast(b)); + (&b)->~B(); // no-warning + } + { + B b; + B a = std::move(b); + b.~B(); // no-warning + } +} + struct MoveOnlyWithDestructor { MoveOnlyWithDestructor(); ~MoveOnlyWithDestructor();