[clang] [LifetimeSafety] Track STL algorithm functions that return lifetimebound iterators (PR #179227)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 2 05:24:56 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-analysis
@llvm/pr-subscribers-clang-temporal-safety
Author: Utkarsh Saxena (usx95)
<details>
<summary>Changes</summary>
Enhanced the `shouldTrackFirstArgument` function in `LifetimeAnnotations.cpp` to recognize standard library algorithm functions like `find`, `find_if`, `search`, etc. that return iterators whose lifetimes are bound to their container arguments. This allows the lifetime checker to detect when these iterators outlive their containers.
The implementation now:
- Checks for standard library algorithm functions that take at least two parameters
- Identifies specific functions by name (find, find_if, find_if_not, etc.)
- Verifies the return type is a GSL pointer type
---
Full diff: https://github.com/llvm/llvm-project/pull/179227.diff
3 Files Affected:
- (modified) clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp (+30-3)
- (modified) clang/test/Sema/Inputs/lifetime-analysis.h (+3)
- (modified) clang/test/Sema/warn-lifetime-analysis-nocfg.cpp (+23)
``````````diff
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index be33caf327802..ac1eb9db8a153 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -174,13 +174,41 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee,
}
bool shouldTrackFirstArgument(const FunctionDecl *FD) {
- if (!FD->getIdentifier() || FD->getNumParams() != 1)
+ if (!FD->getIdentifier() || FD->getNumParams() < 1)
return false;
+ if (!FD->isInStdNamespace())
+ return false;
+ // Track std:: algorithm functions that return an iterator whose lifetime is
+ // bound to the first argument.
+ if (FD->getNumParams() >= 2 && FD->isInStdNamespace() &&
+ isGslPointerType(FD->getReturnType())) {
+ if (llvm::StringSwitch<bool>(FD->getName())
+ .Cases(
+ {
+ "find",
+ "find_if",
+ "find_if_not",
+ "find_first_of",
+ "adjacent_find",
+ "search",
+ "find_end",
+ "lower_bound",
+ "upper_bound",
+ "partition_point",
+ },
+ true)
+ .Default(false))
+ return true;
+ }
const auto *RD = FD->getParamDecl(0)->getType()->getPointeeCXXRecordDecl();
- if (!FD->isInStdNamespace() || !RD || !RD->isInStdNamespace())
+ if (!RD || !RD->isInStdNamespace())
return false;
if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>())
return false;
+
+ if (FD->getNumParams() != 1)
+ return false;
+
if (FD->getReturnType()->isPointerType() ||
isGslPointerType(FD->getReturnType())) {
return llvm::StringSwitch<bool>(FD->getName())
@@ -226,5 +254,4 @@ template <typename T> static bool isRecordWithAttr(QualType Type) {
bool isGslPointerType(QualType QT) { return isRecordWithAttr<PointerAttr>(QT); }
bool isGslOwnerType(QualType QT) { return isRecordWithAttr<OwnerAttr>(QT); }
-
} // namespace clang::lifetimes
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index 4f881d463ebcc..27882e68c1524 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -16,6 +16,9 @@ template<typename T> struct remove_reference { typedef T type; };
template<typename T> struct remove_reference<T &> { typedef T type; };
template<typename T> struct remove_reference<T &&> { typedef T type; };
+template< class InputIt, class T >
+InputIt find( InputIt first, InputIt last, const T& value );
+
template<typename T>
typename remove_reference<T>::type &&move(T &&t) noexcept;
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index c82cf41b07361..a58b446fe4c07 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -429,6 +429,29 @@ int *returnPtrToLocalArray() {
return std::begin(a); // TODO
}
+namespace lifetimebound_stl_algorithms {
+
+std::vector<std::string> GetTemporaryString();
+std::vector<std::string_view> GetTemporaryView();
+
+std::string_view test_str_local() {
+ std::vector<std::string> v;
+ return *std::find(v.begin(), // cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}}
+ v.end(), "42");
+}
+std::string_view test_str_temporary() {
+ return *std::find(GetTemporaryString().begin(), // cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}}
+ GetTemporaryString().end(), "42");
+}
+std::string_view test_view() {
+ std::vector<std::string_view> v;
+ return *std::find(v.begin(), v.end(), "42");
+}
+std::string_view test_view_local() {
+ return *std::find(GetTemporaryView().begin(), GetTemporaryView().end(), "42");
+}
+} // namespace lifetimebound_stl_algorithms
+
struct ptr_wrapper {
std::vector<int>::iterator member;
};
``````````
</details>
https://github.com/llvm/llvm-project/pull/179227
More information about the cfe-commits
mailing list