https://github.com/suoyuan666 updated https://github.com/llvm/llvm-project/pull/208891
>From bdf976c89db45331d800ca62058e83f0751706bb Mon Sep 17 00:00:00 2001 From: Yuan Suo <[email protected]> Date: Sat, 11 Jul 2026 14:54:34 +0800 Subject: [PATCH 1/2] [LifetimeSafety] Introduce buildOriginFlowChain to use-after-invalidation After completing the implementation of `buildOriginFlowChain`, we should integrate it into the remaining diagnostics. I started with `use-after-invalidation` because there is already an overload of `buildOriginFlowChain` for `UseFact`. While testing, I noticed the following diagnostics: ```txt test.cpp:9:23: note: result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v' 9 | auto it = std::find(std::begin(v), std::end(v), 3); // expected-warning {{parameter 'v' is later invalidated}} \ | ^~~~~~~~~~~~~ test.cpp:9:13: note: result of call to 'find<__gnu_cxx::__normal_iterator<int *, std::vector<int>>, int>' aliases the storage of parameter 'v' 9 | auto it = std::find(std::begin(v), std::end(v), 3); // expected-warning {{parameter 'v' is later invalidated}} \ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` I think these diagnostics could be made clearer, but I'd prefer to address that in a separate PR. Signed-off-by: Yuan Suo <[email protected]> --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 14 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 10 +- clang/lib/Sema/SemaLifetimeSafety.h | 14 +- .../Sema/LifetimeSafety/invalidations.cpp | 163 ++++++++++++------ clang/test/Sema/LifetimeSafety/safety.cpp | 8 +- 5 files changed, 138 insertions(+), 71 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index d2827dbca3111..a51ef2f7cc0ba 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -85,12 +85,14 @@ class LifetimeSafetySemaHelper { // Reports when a reference/iterator is used after the container operation // that invalidated it. - virtual void reportUseAfterInvalidation(const Expr *IssueExpr, - const Expr *UseExpr, - const Expr *InvalidationExpr) {} - virtual void reportUseAfterInvalidation(const ParmVarDecl *PVD, - const Expr *UseExpr, - const Expr *InvalidationExpr) {} + virtual void + reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr, + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) {} + virtual void + reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr, + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) {} virtual void reportInvalidatedField(const Expr *IssueExpr, const FieldDecl *Field, const Expr *InvalidationExpr) {} diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index f72f7f80abbc0..5c90e72909a1a 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -263,12 +263,16 @@ class LifetimeChecker { if (Warning.InvalidatedByExpr) { if (IssueExpr) // Use-after-invalidation of an object on stack. - SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(), - Warning.InvalidatedByExpr); + SemaHelper->reportUseAfterInvalidation( + IssueExpr, UF->getUseExpr(), Warning.InvalidatedByExpr, + getExprChain( + LoanPropagation.buildOriginFlowChain(UF, LID, Cfg))); else if (InvalidatedPVD) // Use-after-invalidation of a parameter. SemaHelper->reportUseAfterInvalidation( - InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr); + InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr, + getExprChain( + LoanPropagation.buildOriginFlowChain(UF, LID, Cfg))); } else // Scope-based expiry (use-after-scope). diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 48edf5f3f070f..c659a9d1c4da5 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -188,8 +188,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { << DanglingGlobal->getEndLoc(); } - void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *InvalidationExpr) override { + void + reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr, + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) override { auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr) ? diag::warn_lifetime_safety_use_after_free : diag::warn_lifetime_safety_invalidation; @@ -197,11 +199,14 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { S.Diag(IssueExpr->getExprLoc(), WarnDiag) << InvalidatedSubject << IssueExpr->getSourceRange(); reportInvalidationSite(InvalidationExpr, InvalidatedSubject); + reportAliasingChain(ExprChain); S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) << UseExpr->getSourceRange(); } - void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr, - const Expr *InvalidationExpr) override { + void + reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr, + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) override { auto WarnDiag = isa<CXXDeleteExpr>(InvalidationExpr) ? diag::warn_lifetime_safety_use_after_free @@ -211,6 +216,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { S.Diag(PVD->getSourceRange().getBegin(), WarnDiag) << InvalidatedSubject << PVD->getSourceRange(); reportInvalidationSite(InvalidationExpr, InvalidatedSubject); + reportAliasingChain(ExprChain); S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) << UseExpr->getSourceRange(); } diff --git a/clang/test/Sema/LifetimeSafety/invalidations.cpp b/clang/test/Sema/LifetimeSafety/invalidations.cpp index be1acc6bc7fbc..127e375bc023c 100644 --- a/clang/test/Sema/LifetimeSafety/invalidations.cpp +++ b/clang/test/Sema/LifetimeSafety/invalidations.cpp @@ -7,7 +7,8 @@ bool Bool(); namespace SimpleResize { void IteratorInvalidAfterResize(int new_size) { std::vector<int> v; - auto it = std::begin(v); // expected-warning {{local variable 'v' is later invalidated}} + auto it = std::begin(v); // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of local variable 'v'}} v.resize(new_size); // expected-note {{local variable 'v' is invalidated here}} *it; // expected-note {{later used here}} } @@ -48,7 +49,8 @@ void PointerToContainerTest(std::vector<int>* v) { namespace InvalidateBeforeSwap { void InvalidateBeforeSwapIterators(std::vector<int> v1, std::vector<int> v2) { - auto it1 = std::begin(v1); // expected-warning {{parameter 'v1' is later invalidated}} + auto it1 = std::begin(v1); // expected-warning {{parameter 'v1' is later invalidated}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v1'}} auto it2 = std::begin(v2); if (it1 == std::end(v1) || it2 == std::end(v2)) return; *it1 = 0; // ok @@ -62,7 +64,8 @@ void InvalidateBeforeSwapIterators(std::vector<int> v1, std::vector<int> v2) { } void InvalidateBeforeSwapContainers(std::vector<int> v1, std::vector<int> v2) { - auto it1 = std::begin(v1); // expected-warning {{parameter 'v1' is later invalidated}} + auto it1 = std::begin(v1); // expected-warning {{parameter 'v1' is later invalidated}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v1'}} auto it2 = std::begin(v2); if (it1 == std::end(v1) || it2 == std::end(v2)) return; *it1 = 0; // ok @@ -77,7 +80,8 @@ bool A(); bool B(); void SameConditionInvalidatesThenValidatesIterator() { std::vector<int> container; - auto it = container.begin(); // expected-warning {{local variable 'container' is later invalidated}} + auto it = container.begin(); // expected-warning {{local variable 'container' is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 'container'}} if (it == container.end()) return; const bool a = A(); if (a) { @@ -108,7 +112,9 @@ void MergeWithDifferentContainerValuesInvalidated() { std::vector<int> v1, v2, v3; auto it = std::find(v1.begin(), v1.end(), 10); if (Bool()) { - it = std::find(v2.begin(), v2.end(), 10); // expected-warning {{local variable 'v2' is later invalidated}} + it = std::find(v2.begin(), v2.end(), 10); // expected-warning {{local variable 'v2' is later invalidated}} \ + // expected-note {{result of call to 'find<__gnu_cxx::basic_iterator<int>, int>' aliases the storage of local variable 'v2'}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 'v2'}} } else { it = std::find(v3.begin(), v3.end(), 10); } @@ -119,7 +125,8 @@ void MergeWithDifferentContainerValuesInvalidated() { namespace InvalidationInLoops { void IteratorInvalidationInAForLoop(std::vector<int> v) { - for (auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} + for (auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v'}} it != std::end(v); ++it) { // expected-note {{later used here}} if (Bool()) { @@ -129,7 +136,8 @@ void IteratorInvalidationInAForLoop(std::vector<int> v) { } void IteratorInvalidationInAWhileLoop(std::vector<int> v) { - auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} + auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v'}} while (it != std::end(v)) { if (Bool()) { v.erase(it); // expected-note {{parameter 'v' is invalidated here}} @@ -155,7 +163,8 @@ void NoIteratorInvalidationInAWhileLoopErase(std::unordered_map<int, int> mp) { void IteratorInvalidationInAForeachLoop(std::vector<int> v) { for (int& x : v) { // expected-warning {{parameter 'v' is later invalidated}} \ - // expected-note {{later used here}} + // expected-note {{later used here}} \ + // expected-note {{result of call to 'end' aliases the storage of parameter 'v'}} if (x % 2 == 0) { v.erase(std::find(v.begin(), v.end(), 1)); // expected-note {{parameter 'v' is invalidated here}} } @@ -183,7 +192,9 @@ void IteratorCheckedAfterFind(std::vector<int> v) { } void IteratorCheckedAfterFindThenErased(std::vector<int> v) { - auto it = std::find(std::begin(v), std::end(v), 3); // expected-warning {{parameter 'v' is later invalidated}} + auto it = std::find(std::begin(v), std::end(v), 3); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'find<__gnu_cxx::basic_iterator<int>, int>' aliases the storage of parameter 'v'}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v'}} if (it != std::end(v)) { v.erase(it); // expected-note {{parameter 'v' is invalidated here}} } @@ -201,7 +212,8 @@ void UseReturnedIteratorAfterInsert(std::vector<int> v) { } void UseInvalidIteratorAfterInsert(std::vector<int> v) { - auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} + auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v'}} v.insert(it, 10); // expected-note {{parameter 'v' is invalidated here}} if (it != std::end(v)) { // expected-note {{later used here}} *it; @@ -220,7 +232,8 @@ void IteratorValidAfterInsert(std::vector<int> v) { } void IteratorInvalidAfterInsert(std::vector<int> v, int value) { - auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} + auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v'}} v.insert(it, 0); // expected-note {{parameter 'v' is invalidated here}} *it; // expected-note {{later used here}} } @@ -228,7 +241,8 @@ void IteratorInvalidAfterInsert(std::vector<int> v, int value) { namespace SimpleInvalidIterators { void IteratorUsedAfterErase(std::vector<int> v) { - auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} + auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v'}} for (; it != std::end(v); ++it) { // expected-note {{later used here}} if (*it > 3) { v.erase(it); // expected-note {{parameter 'v' is invalidated here}} @@ -245,7 +259,8 @@ void IteratorUsedAfterPushBackParam(std::vector<int>& v) { // expected-warning { } void IteratorUsedAfterPushBack(std::vector<int> v) { - auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} + auto it = std::begin(v); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of parameter 'v'}} if (it != std::end(v) && *it == 3) { v.push_back(4); // expected-note {{parameter 'v' is invalidated here}} } @@ -254,59 +269,70 @@ void IteratorUsedAfterPushBack(std::vector<int> v) { void IteratorUsedAfterPreIncrement() { std::vector<int> v; - auto it = v.begin(); // expected-warning {{local variable 'v' is later invalidated}} - auto next = ++it; + auto it = v.begin(); // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 'v'}} + auto next = ++it; // expected-note {{expression aliases the storage of local variable 'v'}} v.push_back(1); // expected-note {{local variable 'v' is invalidated here}} (void)*next; // expected-note {{later used here}} } void IteratorUsedAfterPostDecrement(std::vector<int> v) { - auto it = v.rbegin(); // expected-warning {{parameter 'v' is later invalidated}} - auto prev = it--; + auto it = v.rbegin(); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'rbegin' aliases the storage of parameter 'v'}} + auto prev = it--; // expected-note {{expression aliases the storage of parameter 'v'}} v.push_back(1); // expected-note {{parameter 'v' is invalidated here}} (void)*prev; // expected-note {{later used here}} } void IteratorUsedAfterAddition() { std::vector<int> v; - auto it = v.cbegin(); // expected-warning {{local variable 'v' is later invalidated}} - auto next = it + 5; + auto it = v.cbegin(); // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{result of call to 'cbegin' aliases the storage of local variable 'v'}} + auto next = it + 5; // expected-note {{expression aliases the storage of local variable 'v'}} v.push_back(1); // expected-note {{local variable 'v' is invalidated here}} (void)*next; // expected-note {{later used here}} } void IteratorUsedAfterReverseSubtraction(std::vector<int> v) { - auto it = v.crbegin(); // expected-warning {{parameter 'v' is later invalidated}} - auto prev = 5 - it; + auto it = v.crbegin(); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'crbegin' aliases the storage of parameter 'v'}} + auto prev = 5 - it; // expected-note {{local variable 'it' aliases the storage of parameter 'v'}} \ + // expected-note {{expression aliases the storage of parameter 'v'}} v.push_back(1); // expected-note {{parameter 'v' is invalidated here}} (void)*prev; // expected-note {{later used here}} } void IteratorUsedAfterAddAdd(std::vector<int> v) { - auto it = v.cbegin(); // expected-warning {{parameter 'v' is later invalidated}} - auto next = (it + 5) + 5; + auto it = v.cbegin(); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'cbegin' aliases the storage of parameter 'v'}} + auto next = (it + 5) + 5; // expected-note 2 {{expression aliases the storage of parameter 'v'}} v.push_back(1); // expected-note {{parameter 'v' is invalidated here}} (void)*next; // expected-note {{later used here}} } void IteratorUsedAfterMixedAddition() { std::vector<int> v; - auto it = v.cbegin(); // expected-warning {{local variable 'v' is later invalidated}} - auto next = 1 + it + 2 + 3; + auto it = v.cbegin(); // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{result of call to 'cbegin' aliases the storage of local variable 'v'}} + auto next = 1 + it + 2 + 3; // expected-note 3 {{expression aliases the storage of local variable 'v'}} \ + // expected-note {{local variable 'it' aliases the storage of local variable 'v'}} v.push_back(1); // expected-note {{local variable 'v' is invalidated here}} (void)*next; // expected-note {{later used here}} } void IteratorUsedAfterPreIncrementAddAssign(std::vector<int> v) { - auto it = v.begin(); // expected-warning {{parameter 'v' is later invalidated}} - it = ++it + 1 + 2; + auto it = v.begin(); // expected-warning {{parameter 'v' is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of parameter 'v'}} + it = ++it + 1 + 2; // expected-note 3 {{expression aliases the storage of parameter 'v'}} v.push_back(1); // expected-note {{parameter 'v' is invalidated here}} (void)*it; // expected-note {{later used here}} } void IteratorUsedAfterBeginAddAssign() { std::vector<int> v; - auto it = v.begin() + 1; // expected-warning {{local variable 'v' is later invalidated}} + auto it = v.begin() + 1; // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{expression aliases the storage of local variable 'v'}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 'v'}} v.push_back(1); // expected-note {{local variable 'v' is invalidated here}} (void)*it; // expected-note {{later used here}} } @@ -314,7 +340,9 @@ void IteratorUsedAfterBeginAddAssign() { void IteratorUsedAfterStdBeginAddAssign() { std::vector<int> v; std::vector<int>::iterator it; - it = std::begin(v) + 1; // expected-warning {{local variable 'v' is later invalidated}} + it = std::begin(v) + 1; // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{expression aliases the storage of local variable 'v'}} \ + // expected-note {{result of call to 'begin<std::vector<int>>' aliases the storage of local variable 'v'}} v.push_back(1); // expected-note {{local variable 'v' is invalidated here}} (void)*it; // expected-note {{later used here}} } @@ -324,13 +352,14 @@ namespace InvalidatingThroughContainerAliases { void IteratorInvalidatedThroughLocalReferenceAlias() { std::vector<int> vv; std::vector<int> &v = vv; - auto it = vv.begin(); // expected-warning {{local variable 'vv' is later invalidated}} + auto it = vv.begin(); // expected-warning {{local variable 'vv' is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 'vv'}} v.push_back(42); // expected-note {{local variable 'vv' is invalidated here}} (void)it; // expected-note {{later used here}} } void IteratorInvalidatedThroughPointerParameter(std::vector<int> *v) { // expected-warning {{parameter 'v' is later invalidated}} - auto it = v->begin(); + auto it = v->begin(); // expected-note {{result of call to 'begin' aliases the storage of parameter 'v'}} v->push_back(42); // expected-note {{parameter 'v' is invalidated here}} (void)it; // expected-note {{later used here}} } @@ -374,7 +403,8 @@ namespace ElementReferences { void ReferenceToVectorElement() { std::vector<int> v = {1, 2, 3}; - int& ref = v[0]; // expected-warning {{local variable 'v' is later invalidated}} + int& ref = v[0]; // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{expression aliases the storage of local variable 'v'}} v.push_back(4); // expected-note {{local variable 'v' is invalidated here}} ref = 10; // expected-note {{later used here}} (void)ref; @@ -382,14 +412,16 @@ void ReferenceToVectorElement() { void PointerRefToVectorElement() { std::vector<int*> v = {nullptr, nullptr}; - int*& ref = v[0]; // expected-warning {{local variable 'v' is later invalidated}} + int*& ref = v[0]; // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{expression aliases the storage of local variable 'v'}} v.push_back(nullptr); // expected-note {{local variable 'v' is invalidated here}} ref = nullptr; // expected-note {{later used here}} } void PointerToVectorElement() { std::vector<int> v = {1, 2, 3}; - int* ptr = &v[0]; // expected-warning {{local variable 'v' is later invalidated}} + int* ptr = &v[0]; // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{expression aliases the storage of local variable 'v'}} v.resize(100); // expected-note {{local variable 'v' is invalidated here}} *ptr = 10; // expected-note {{later used here}} } @@ -415,13 +447,15 @@ void SelfInvalidatingMap() { // expected-note {{local variable 'mp' is invalidated here}} \ // expected-note {{later used here}} \ // expected-note {{local variable 'mp' is invalidated here}} \ + // expected-note {{expression aliases the storage of local variable 'mp'}} \ // expected-note {{later used here}} } void InvalidateErase() { std::flat_map<int, std::string> mp; // None of these containers provide iterator stability. So following is unsafe: - auto it = mp.find(3); // expected-warning {{local variable 'mp' is later invalidated}} + auto it = mp.find(3); // expected-warning {{local variable 'mp' is later invalidated}} \ + // expected-note {{result of call to 'find' aliases the storage of local variable 'mp'}} mp.erase(mp.find(4)); // expected-note {{local variable 'mp' is invalidated here}} if (it != mp.end()) // expected-note {{later used here}} *it; @@ -488,7 +522,8 @@ void ConditionalFieldInvalidatesIterator(bool flag) { // FIXME: Requires field-sensitive AccessPaths to fix. void Invalidate1Use2ViaRefIsOk() { S s; - auto it = s.strings2.begin(); // expected-warning {{local variable 's' is later invalidated}} + auto it = s.strings2.begin(); // expected-warning {{local variable 's' is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 's'}} auto& strings1 = s.strings1; strings1.push_back("1"); // expected-note {{local variable 's' is invalidated here}} *it; // expected-note {{later used here}} @@ -509,7 +544,8 @@ void PointerToContainerIsOk() { void IteratorFromPointerToContainerIsInvalidated() { std::vector<std::string> s; std::vector<std::string>* p = &s; // expected-warning {{local variable 's' is later invalidated}} - auto it = p->begin(); + auto it = p->begin(); // expected-note {{local variable 'p' aliases the storage of local variable 's'}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 's'}} p->push_back("1"); // expected-note {{local variable 's' is invalidated here}} *it; // expected-note {{later used here}} } @@ -517,7 +553,8 @@ void IteratorFromPointerToContainerIsInvalidated() { // iterators into the outer container. void ChangingRegionOwnedByContainerIsOk() { std::vector<std::string> subdirs; - for (std::string& path : subdirs) // expected-warning {{local variable 'subdirs' is later invalidated}} expected-note {{later used here}} + for (std::string& path : subdirs) // expected-warning {{local variable 'subdirs' is later invalidated}} expected-note {{later used here}} \ + // expected-note {{result of call to 'end' aliases the storage of local variable 'subdirs'}} path = std::string(); // expected-note {{local variable 'subdirs' is invalidated here}} } @@ -724,14 +761,16 @@ void SetExtractDoesNotInvalidateOthers() { void SetClearInvalidates() { std::set<int> s; - auto it = s.begin(); // expected-warning {{local variable 's' is later invalidated}} + auto it = s.begin(); // expected-warning {{local variable 's' is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 's'}} s.clear(); // expected-note {{local variable 's' is invalidated here}} *it; // expected-note {{later used here}} } void MapClearInvalidates() { std::map<int, int> m; - auto it = m.begin(); // expected-warning {{local variable 'm' is later invalidated}} + auto it = m.begin(); // expected-warning {{local variable 'm' is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 'm'}} m.clear(); // expected-note {{local variable 'm' is invalidated here}} *it; // expected-note {{later used here}} } @@ -759,6 +798,7 @@ void FlatMapSubscriptMultipleCallsInvalidate(std::flat_map<int, int> mp, int a, // expected-note {{parameter 'mp' is invalidated here}} \ // expected-note {{later used here}} \ // expected-note {{parameter 'mp' is invalidated here}} \ + // expected-note 2 {{expression aliases the storage of parameter 'mp'}} \ // expected-note {{later used here}} } @@ -768,7 +808,7 @@ namespace lambda_capture_invalidation { void captured_view_invalidated_by_owner() { std::string s = "42"; std::string_view p = s; // expected-warning {{local variable 's' is later invalidated}} - auto lambda = [=]() { return p; }; + auto lambda = [=]() { return p; }; // expected-note {{local variable 'p' aliases the storage of local variable 's'}} s.push_back('c'); // expected-note {{local variable 's' is invalidated here}} lambda(); // expected-note {{later used here}} } @@ -776,7 +816,7 @@ void captured_view_invalidated_by_owner() { void multiple_captures_one_invalidated() { std::string s1 = "a", s2 = "b"; std::string_view p1 = s1, p2 = s2; // expected-warning {{local variable 's1' is later invalidated}} - auto lambda = [=]() { return p1.size() + p2.size(); }; + auto lambda = [=]() { return p1.size() + p2.size(); }; // expected-note {{local variable 'p1' aliases the storage of local variable 's1'}} s1.clear(); // expected-note {{local variable 's1' is invalidated here}} lambda(); // expected-note {{later used here}} } @@ -810,7 +850,8 @@ struct S { void bar(); void baz(){ std::vector<std::string> vec = {"42"}; - v = vec[0]; // expected-warning {{local variable 'vec' is later invalidated}} + v = vec[0]; // expected-warning {{local variable 'vec' is later invalidated}} \ + // expected-note {{expression aliases the storage of local variable 'vec'}} vec.push_back("1"); // expected-note {{local variable 'vec' is invalidated here}} bar(); // expected-note {{later used here}} v = nullptr; @@ -823,7 +864,8 @@ namespace callable_wrappers { void function_captured_ref_invalidated() { std::vector<int> v; v.push_back(1); - std::function<void()> f = [&r = v[0]]() { (void)r; }; // expected-warning {{local variable 'v' is later invalidated}} + std::function<void()> f = [&r = v[0]]() { (void)r; }; // expected-warning {{local variable 'v' is later invalidated}} \ + // expected-note {{expression aliases the storage of local variable 'v'}} v.push_back(2); // expected-note {{local variable 'v' is invalidated here}} (void)f; // expected-note {{later used here}} } @@ -835,7 +877,8 @@ namespace explicit_destructor { void explicit_destructor_invalidates_pointer() { std::string s = "42"; - const char *p = s.data(); // expected-warning {{local variable 's' is later invalidated}} + const char *p = s.data(); // expected-warning {{local variable 's' is later invalidated}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 's'}} s.~basic_string(); // expected-note {{local variable 's' is invalidated here}} (void)*p; // expected-note {{later used here}} } @@ -843,7 +886,8 @@ void explicit_destructor_invalidates_pointer() { void pointer_destructor_invalidates_pointer() { char storage[sizeof(std::string)]; std::string *obj = new (storage) std::string("42"); // expected-warning {{local variable 'storage' is later invalidated}} - const char *p = obj->data(); + const char *p = obj->data(); // expected-note {{local variable 'obj' aliases the storage of local variable 'storage'}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 'storage'}} obj->~basic_string(); // expected-note {{local variable 'storage' is invalidated here}} (void)*p; // expected-note {{later used here}} } @@ -851,7 +895,8 @@ void pointer_destructor_invalidates_pointer() { void destroy_at_invalidates_pointer() { char storage[sizeof(std::string)]; std::string *obj = new (storage) std::string("42"); // expected-warning {{local variable 'storage' is later invalidated}} - const char *p = obj->data(); + const char *p = obj->data(); // expected-note {{local variable 'obj' aliases the storage of local variable 'storage'}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 'storage'}} std::destroy_at(obj); // expected-note {{local variable 'storage' is invalidated here}} (void)*p; // expected-note {{later used here}} } @@ -869,7 +914,8 @@ void destroy_at_then_placement_new_rescues_pointer() { void destroy_at_invalidates_array_pointer() { std::string arr[1] = {"42"}; std::string (&arr_ref)[1] = arr; - const char *p = arr[0].data(); // expected-warning {{local variable 'arr' is later invalidated}} + const char *p = arr[0].data(); // expected-warning {{local variable 'arr' is later invalidated}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 'arr'}} std::destroy_at(&arr_ref); // expected-note {{local variable 'arr' is invalidated here}} (void)*p; // expected-note {{later used here}} } @@ -877,7 +923,8 @@ void destroy_at_invalidates_array_pointer() { void reference_destructor_invalidates_pointer() { std::string s = "42"; std::string &ref = s; // expected-warning {{local variable 's' is later invalidated}} - const char *p = ref.data(); + const char *p = ref.data(); // expected-note {{local variable 'ref' aliases the storage of local variable 's'}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 's'}} std::destroy_at(&ref); // expected-note {{local variable 's' is invalidated here}} (void)*p; // expected-note {{later used here}} } @@ -885,7 +932,8 @@ void reference_destructor_invalidates_pointer() { void destroy_at_ternary_operator(bool flag) { std::string* str1 = new std::string; // expected-warning {{allocated object is later invalidated}} std::string* str2 = new std::string; - const char *p = str1->data(); + const char *p = str1->data(); // expected-note {{local variable 'str1' aliases the storage of allocated object}} \ + // expected-note {{result of call to 'data' aliases the storage of allocated object}} std::destroy_at(flag ? str1 : str2); // expected-note {{allocated object is invalidated here}} (void)*p; // expected-note {{later used here}} } @@ -897,7 +945,8 @@ struct StringOwner { // FIXME: False-positive void member_destructor_invalidates_pointer() { StringOwner owner = {"42", "43"}; - const char *p = owner.s.data(); // expected-warning {{local variable 'owner' is later invalidated}} + const char *p = owner.s.data(); // expected-warning {{local variable 'owner' is later invalidated}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 'owner'}} owner.t.~basic_string(); // expected-note {{local variable 'owner' is invalidated here}} (void)*p; // expected-note {{later used here}} } @@ -908,7 +957,8 @@ namespace unique_ptr_invalidation { void invalid_after_reset() { std::unique_ptr<int> up(new int); - int *p = up.get(); // expected-warning {{local variable 'up' is later invalidated}} + int *p = up.get(); // expected-warning {{local variable 'up' is later invalidated}} \ + // expected-note {{result of call to 'get' aliases the storage of local variable 'up'}} up.reset(); // expected-note {{local variable 'up' is invalidated here}} (void)*p; // expected-note {{later used here}} } @@ -916,14 +966,16 @@ void invalid_after_reset() { void invalid_after_move_assign() { std::unique_ptr<int> up(new int); std::unique_ptr<int> other(new int); - int *p = up.get(); // expected-warning {{local variable 'up' is later invalidated}} + int *p = up.get(); // expected-warning {{local variable 'up' is later invalidated}} \ + // expected-note {{result of call to 'get' aliases the storage of local variable 'up'}} up = std::move(other); // expected-note {{local variable 'up' is invalidated here}} (void)*p; // expected-note {{later used here}} } void invalid_after_null_assign() { std::unique_ptr<int> up(new int); - int *p = up.get(); // expected-warning {{local variable 'up' is later invalidated}} + int *p = up.get(); // expected-warning {{local variable 'up' is later invalidated}} \ + // expected-note {{result of call to 'get' aliases the storage of local variable 'up'}} up = nullptr; // expected-note {{local variable 'up' is invalidated here}} (void)*p; // expected-note {{later used here}} } @@ -931,7 +983,8 @@ void invalid_after_null_assign() { void invalid_after_ternary_reset(bool flag) { std::unique_ptr<int> up(new int); std::unique_ptr<int> other(new int); - int *p = flag ? up.get() : other.get(); // expected-warning {{local variable 'up' is later invalidated}} + int *p = flag ? up.get() : other.get(); // expected-warning {{local variable 'up' is later invalidated}} \ + // expected-note {{result of call to 'get' aliases the storage of local variable 'up'}} up.reset(); // expected-note {{local variable 'up' is invalidated here}} (void)*p; // expected-note {{later used here}} } diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp index 0776fcdf05773..f3b042cce74b2 100644 --- a/clang/test/Sema/LifetimeSafety/safety.cpp +++ b/clang/test/Sema/LifetimeSafety/safety.cpp @@ -3282,7 +3282,7 @@ void delete_direct_use_after_free() { void delete_alias_use_after_free() { MyObj *p = new MyObj; // expected-warning {{allocated object does not live long enough}} - MyObj *q = p; + MyObj *q = p; // expected-note {{local variable 'p' aliases the storage of allocated object}} delete p; // expected-note {{allocated object is freed here}} (void)q->id; // expected-note {{later used here}} } @@ -3430,7 +3430,7 @@ void placement_new_array_braces() { void placement_new_heap_then_delete_use_after_free() { int *storage = new int(7); // expected-warning {{allocated object does not live long enough}} - int *p = new (storage) int(42); + int *p = new (storage) int(42); // expected-note {{local variable 'storage' aliases the storage of allocated object}} delete storage; // expected-note {{allocated object is freed here}} (void)*p; // expected-note {{later used here}} } @@ -3478,7 +3478,9 @@ void placement_new_delete_result_of_lifetimebound_call() { int *x = new int(1); // expected-warning {{allocated object does not live long enough}} int *y = new int(2); // expected-warning {{allocated object does not live long enough}} int *slot = nullptr; - int **p = new (&slot) int *(foo(x, y)); + int **p = new (&slot) int *(foo(x, y)); // expected-note {{local variable 'x' aliases the storage of allocated object}} \ + // expected-note {{local variable 'y' aliases the storage of allocated object}} \ + // expected-note 2 {{result of call to 'foo' aliases the storage of allocated object}} delete foo(x, y); // expected-note 2 {{allocated object is freed here}} (void)**p; // expected-note 2 {{later used here}} } >From 8544be241ac3fc91f476c27c3e403a76e6d9eb12 Mon Sep 17 00:00:00 2001 From: Yuan Suo <[email protected]> Date: Thu, 16 Jul 2026 20:43:20 +0800 Subject: [PATCH 2/2] Reduce the number of calls to getExprChain Signed-off-by: Yuan Suo <[email protected]> --- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 5c90e72909a1a..53e5077131147 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -260,25 +260,24 @@ class LifetimeChecker { SourceLocation ExpiryLoc = Warning.ExpiryLoc; if (const auto *UF = CausingFact.dyn_cast<const UseFact *>()) { + llvm::SmallVector<const Expr *> ExprChain = + getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg)); if (Warning.InvalidatedByExpr) { if (IssueExpr) // Use-after-invalidation of an object on stack. - SemaHelper->reportUseAfterInvalidation( - IssueExpr, UF->getUseExpr(), Warning.InvalidatedByExpr, - getExprChain( - LoanPropagation.buildOriginFlowChain(UF, LID, Cfg))); + SemaHelper->reportUseAfterInvalidation(IssueExpr, UF->getUseExpr(), + Warning.InvalidatedByExpr, + ExprChain); else if (InvalidatedPVD) // Use-after-invalidation of a parameter. SemaHelper->reportUseAfterInvalidation( InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr, - getExprChain( - LoanPropagation.buildOriginFlowChain(UF, LID, Cfg))); + ExprChain); } else // Scope-based expiry (use-after-scope). - SemaHelper->reportUseAfterScope( - IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc, - getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg))); + SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(), + MovedExpr, ExpiryLoc, ExprChain); } else if (const auto *OEF = CausingFact.dyn_cast<const OriginEscapesFact *>()) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
