Author: NeKon69 Date: 2026-04-14T12:34:04+05:30 New Revision: 78cd6c9b28f99e201bf44c84044517820f32305e
URL: https://github.com/llvm/llvm-project/commit/78cd6c9b28f99e201bf44c84044517820f32305e DIFF: https://github.com/llvm/llvm-project/commit/78cd6c9b28f99e201bf44c84044517820f32305e.diff LOG: [LifetimeSafety] Detect use-after-scope through fields in member calls (#191731) Add `UseFact`s for field origins when calling instance methods. Fixes #182945 --------- Co-authored-by: Utkarsh Saxena <[email protected]> Added: Modified: clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp clang/test/Sema/warn-lifetime-safety-invalidations.cpp clang/test/Sema/warn-lifetime-safety.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h index cab524f12bab3..3cf5971973c2a 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h @@ -75,6 +75,10 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> { void handleExitBlock(); + /// Mark all fields of the implicit object as used for an instance method + /// call, since the callee may access any part of the object. + void handleImplicitObjectFieldUses(const Expr *Call, const FunctionDecl *FD); + void handleGSLPointerConstruction(const CXXConstructExpr *CCE); /// Detects arguments passed to rvalue reference parameters and creates diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 2a2ef88987286..cae56ddd3d7c3 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -728,6 +728,38 @@ void FactsGenerator::handleInvalidatingCall(const Expr *Call, ThisList->getOuterOriginID(), Call)); } +void FactsGenerator::handleImplicitObjectFieldUses(const Expr *Call, + const FunctionDecl *FD) { + const auto *MemberCall = dyn_cast_or_null<CXXMemberCallExpr>(Call); + if (!MemberCall) + return; + + if (!isa_and_present<CXXThisExpr>( + MemberCall->getImplicitObjectArgument()->IgnoreImpCasts())) + return; + + const auto *MD = dyn_cast<CXXMethodDecl>(FD); + assert(MD && "Function must be a CXXMethodDecl for member calls"); + + const auto *ClassDecl = MD->getParent()->getDefinition(); + if (!ClassDecl) + return; + + const auto UseFields = [&](const CXXRecordDecl *RD) { + for (const auto *Field : RD->fields()) + if (auto *FieldList = getOriginsList(*Field)) + CurrentBlockFacts.push_back( + FactMgr.createFact<UseFact>(Call, FieldList)); + }; + + UseFields(ClassDecl); + + ClassDecl->forallBases([&](const CXXRecordDecl *Base) { + UseFields(Base); + return true; + }); +} + void FactsGenerator::handleFunctionCall(const Expr *Call, const FunctionDecl *FD, ArrayRef<const Expr *> Args, @@ -742,6 +774,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, handleUse(Arg); handleInvalidatingCall(Call, FD, Args); handleMovedArgsInCall(FD, Args); + handleImplicitObjectFieldUses(Call, FD); if (!CallList) return; auto IsArgLifetimeBound = [FD](unsigned I) -> bool { diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp index c7f920c03736a..5a77ac97d16b1 100644 --- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp +++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp @@ -512,3 +512,18 @@ void ref_capture_reassigned_to_safe() { lambda(); // should not warn } } // namespace lambda_capture_invalidation + +namespace method_call_uses_field_invalidation { + +struct S { + std::string_view v; + void bar(); + void baz(){ + std::vector<std::string> vec = {"42"}; + v = vec[0]; // expected-warning {{object whose reference is captured is later invalidated}} + vec.push_back("1"); // expected-note {{invalidated here}} + bar(); // expected-note {{later used here}} + v = nullptr; + } +}; +} // namespace method_call_uses_field_invalidation diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 2aca44daeb0aa..f87b5cbdd0230 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -2532,6 +2532,73 @@ int *noreturn_dead_nested(bool cond, bool cond2, int *num) { } // namespace conditional_operator_control_flow +namespace method_call_uses_field_origins { +int GLOBAL_INT; +std::string GLOBAL_STRING{"123"}; + +struct S { + int* p_; + void bar(); + void foo() { + { + int num; + this->p_ = # // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + bar(); // expected-note {{later used here}} + this->p_ = &GLOBAL_INT; + } + void baz() { + { + int num; + this->p_ = # + } + this->p_ = &GLOBAL_INT; + bar(); + } +}; + +struct T { + std::string_view v; + void bar(); + void foo() { + v = std::string("tmp"); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + bar(); // expected-note {{later used here}} + } +}; + +// FIXME: false-negative +void foo() { + S s; + { + int num; + s.p_ = # // does not warn + } + s.bar(); + s.p_ = &GLOBAL_INT; +} + +struct S2 : S { + void bar2(); + void foo2() { + { + int num; + this->p_ = # // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + bar(); // expected-note {{later used here}} + this->p_ = &GLOBAL_INT; + } + void baz2() { + { + int num; + this->p_ = # // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + bar2(); // expected-note {{later used here}} + this->p_ = nullptr; + } +}; + +} // namespace method_call_uses_field_origins + namespace CXXDefaultInitExprTests { struct Holder { std::string_view view = std::string("temporary"); // expected-warning {{address of stack memory escapes to a field}} expected-note {{this field dangles}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
