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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
