CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
923 lines
33 KiB
C++
923 lines
33 KiB
C++
// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify %s
|
|
|
|
#include "Inputs/lifetime-analysis.h"
|
|
|
|
bool Bool();
|
|
|
|
namespace SimpleResize {
|
|
void IteratorInvalidAfterResize(int new_size) {
|
|
std::vector<int> v;
|
|
auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.resize(new_size); // expected-note {{invalidated here}}
|
|
*it; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorValidAfterResize(int new_size) {
|
|
std::vector<int> v;
|
|
auto it = std::begin(v);
|
|
v.resize(new_size);
|
|
it = std::begin(v);
|
|
if (it != std::end(v)) {
|
|
*it; // ok
|
|
}
|
|
}
|
|
} // namespace SimpleResize
|
|
|
|
namespace CheckModel {
|
|
void IteratorValidAfterCheck() {
|
|
std::vector<int> v;
|
|
auto it = v.begin();
|
|
*it; // ok
|
|
}
|
|
} // namespace CheckModel
|
|
|
|
namespace PointerToContainer {
|
|
std::vector<int>* GetContainerPointer();
|
|
void PointerToContainerTest() {
|
|
// FIXME: Use opaque loans.
|
|
std::vector<int>* v = GetContainerPointer();
|
|
auto it = v->begin();
|
|
*it = 0; // not-ok
|
|
}
|
|
void PointerToContainerTest(std::vector<int>* v) {
|
|
// FIXME: Handle placeholder loans.
|
|
auto it = v->begin();
|
|
*it = 0; // not-ok
|
|
}
|
|
} // namespace PointerToContainer
|
|
|
|
namespace InvalidateBeforeSwap {
|
|
void InvalidateBeforeSwapIterators(std::vector<int> v1, std::vector<int> v2) {
|
|
auto it1 = std::begin(v1); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto it2 = std::begin(v2);
|
|
if (it1 == std::end(v1) || it2 == std::end(v2)) return;
|
|
*it1 = 0; // ok
|
|
*it2 = 0; // ok
|
|
v1.clear(); // expected-note {{invalidated here}}
|
|
*it1 = 0; // expected-note {{later used here}}
|
|
// FIXME: Handle invalidating functions like std::swap.
|
|
std::swap(it1, it2);
|
|
*it1 = 0; // ok
|
|
*it2 = 0; // not-ok
|
|
}
|
|
|
|
void InvalidateBeforeSwapContainers(std::vector<int> v1, std::vector<int> v2) {
|
|
auto it1 = std::begin(v1); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto it2 = std::begin(v2);
|
|
if (it1 == std::end(v1) || it2 == std::end(v2)) return;
|
|
*it1 = 0; // ok
|
|
*it2 = 0; // ok
|
|
v1.clear(); // expected-note {{invalidated here}}
|
|
*it1 = 0; // expected-note {{later used here}}
|
|
}
|
|
} // namespace InvalidateBeforeSwap
|
|
|
|
namespace MergeConditionBasic {
|
|
bool A();
|
|
bool B();
|
|
void SameConditionInvalidatesThenValidatesIterator() {
|
|
std::vector<int> container;
|
|
auto it = container.begin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
if (it == container.end()) return;
|
|
const bool a = A();
|
|
if (a) {
|
|
container.clear(); // expected-note {{invalidated here}}
|
|
}
|
|
if (a) {
|
|
it = container.begin();
|
|
if (it == std::end(container)) return;
|
|
}
|
|
*it = 10; // expected-note {{later used here}}
|
|
}
|
|
} // namespace MergeConditionBasic
|
|
|
|
namespace IteratorWithMultipleContainers {
|
|
void MergeWithDifferentContainerValuesIteratorNotInvalidated() {
|
|
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);
|
|
} else {
|
|
it = std::find(v3.begin(), v3.end(), 10);
|
|
}
|
|
v1.clear();
|
|
*it = 20;
|
|
}
|
|
|
|
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 {{object whose reference is captured is later invalidated}}
|
|
} else {
|
|
it = std::find(v3.begin(), v3.end(), 10);
|
|
}
|
|
v2.clear(); // expected-note {{invalidated here}}
|
|
*it = 20; // expected-note {{later used here}}
|
|
}
|
|
} // namespace IteratorWithMultipleContainers
|
|
|
|
namespace InvalidationInLoops {
|
|
void IteratorInvalidationInAForLoop(std::vector<int> v) {
|
|
for (auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
it != std::end(v);
|
|
++it) { // expected-note {{later used here}}
|
|
if (Bool()) {
|
|
v.erase(it); // expected-note {{invalidated here}}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IteratorInvalidationInAWhileLoop(std::vector<int> v) {
|
|
auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
while (it != std::end(v)) {
|
|
if (Bool()) {
|
|
v.erase(it); // expected-note {{invalidated here}}
|
|
}
|
|
++it; // expected-note {{later used here}}
|
|
}
|
|
}
|
|
|
|
void NoIteratorInvalidationInAWhileLoopErase(std::unordered_map<int, int> mp) {
|
|
auto it = mp.begin();
|
|
while (it != std::end(mp)) {
|
|
if (Bool()) {
|
|
auto next = it;
|
|
++next;
|
|
mp.erase(it); // Ok. 'next' remains valid.
|
|
it = next;
|
|
}
|
|
else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
void IteratorInvalidationInAForeachLoop(std::vector<int> v) {
|
|
for (int& x : v) { // expected-warning {{object whose reference is captured is later invalidated}} \
|
|
// expected-note {{later used here}}
|
|
if (x % 2 == 0) {
|
|
v.erase(std::find(v.begin(), v.end(), 1)); // expected-note {{invalidated here}}
|
|
}
|
|
}
|
|
}
|
|
} // namespace InvalidationInLoops
|
|
|
|
namespace StdVectorPopBack {
|
|
void StdVectorPopBackDoesNotInvalidateOthers(std::vector<int> v) {
|
|
auto it = v.begin();
|
|
if (it == v.end()) return;
|
|
*it;
|
|
v.pop_back();
|
|
*it;
|
|
}
|
|
} // namespace StdVectorPopBack
|
|
|
|
|
|
namespace SimpleStdFind {
|
|
void IteratorCheckedAfterFind(std::vector<int> v) {
|
|
auto it = std::find(std::begin(v), std::end(v), 3);
|
|
if (it != std::end(v)) {
|
|
*it; // ok
|
|
}
|
|
}
|
|
|
|
void IteratorCheckedAfterFindThenErased(std::vector<int> v) {
|
|
auto it = std::find(std::begin(v), std::end(v), 3); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
if (it != std::end(v)) {
|
|
v.erase(it); // expected-note {{invalidated here}}
|
|
}
|
|
*it; // expected-note {{later used here}}
|
|
}
|
|
} // namespace SimpleStdFind
|
|
|
|
namespace SimpleInsert {
|
|
void UseReturnedIteratorAfterInsert(std::vector<int> v) {
|
|
auto it = std::begin(v);
|
|
it = v.insert(it, 10);
|
|
if (it != std::end(v)) {
|
|
*it; // ok
|
|
}
|
|
}
|
|
|
|
void UseInvalidIteratorAfterInsert(std::vector<int> v) {
|
|
auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.insert(it, 10); // expected-note {{invalidated here}}
|
|
if (it != std::end(v)) { // expected-note {{later used here}}
|
|
*it;
|
|
}
|
|
}
|
|
} // namespace SimpleInsert
|
|
|
|
namespace SimpleStdInsert {
|
|
void IteratorValidAfterInsert(std::vector<int> v) {
|
|
auto it = std::begin(v);
|
|
v.insert(it, 0);
|
|
it = std::begin(v);
|
|
if (it != std::end(v)) {
|
|
*it; // ok
|
|
}
|
|
}
|
|
|
|
void IteratorInvalidAfterInsert(std::vector<int> v, int value) {
|
|
auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.insert(it, 0); // expected-note {{invalidated here}}
|
|
*it; // expected-note {{later used here}}
|
|
}
|
|
} // namespace SimpleStdInsert
|
|
|
|
namespace SimpleInvalidIterators {
|
|
void IteratorUsedAfterErase(std::vector<int> v) {
|
|
auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
for (; it != std::end(v); ++it) { // expected-note {{later used here}}
|
|
if (*it > 3) {
|
|
v.erase(it); // expected-note {{invalidated here}}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IteratorUsedAfterPushBackParam(std::vector<int>& v) { // expected-warning {{parameter is later invalidated}}
|
|
auto it = std::begin(v);
|
|
if (it != std::end(v) && *it == 3) {
|
|
v.push_back(4); // expected-note {{invalidated here}}
|
|
}
|
|
++it; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterPushBack(std::vector<int> v) {
|
|
auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
if (it != std::end(v) && *it == 3) {
|
|
v.push_back(4); // expected-note {{invalidated here}}
|
|
}
|
|
++it; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterPreIncrement() {
|
|
std::vector<int> v;
|
|
auto it = v.begin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto next = ++it;
|
|
v.push_back(1); // expected-note {{invalidated here}}
|
|
(void)*next; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterPostDecrement(std::vector<int> v) {
|
|
auto it = v.rbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto prev = it--;
|
|
v.push_back(1); // expected-note {{invalidated here}}
|
|
(void)*prev; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterAddition() {
|
|
std::vector<int> v;
|
|
auto it = v.cbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto next = it + 5;
|
|
v.push_back(1); // expected-note {{invalidated here}}
|
|
(void)*next; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterReverseSubtraction(std::vector<int> v) {
|
|
auto it = v.crbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto prev = 5 - it;
|
|
v.push_back(1); // expected-note {{invalidated here}}
|
|
(void)*prev; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterAddAdd(std::vector<int> v) {
|
|
auto it = v.cbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto next = (it + 5) + 5;
|
|
v.push_back(1); // expected-note {{invalidated here}}
|
|
(void)*next; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterMixedAddition() {
|
|
std::vector<int> v;
|
|
auto it = v.cbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto next = 1 + it + 2 + 3;
|
|
v.push_back(1); // expected-note {{invalidated here}}
|
|
(void)*next; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterPreIncrementAddAssign(std::vector<int> v) {
|
|
auto it = v.begin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
it = ++it + 1 + 2;
|
|
v.push_back(1); // expected-note {{invalidated here}}
|
|
(void)*it; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterBeginAddAssign() {
|
|
std::vector<int> v;
|
|
auto it = v.begin() + 1; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.push_back(1); // expected-note {{invalidated here}}
|
|
(void)*it; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorUsedAfterStdBeginAddAssign() {
|
|
std::vector<int> v;
|
|
std::vector<int>::iterator it;
|
|
it = std::begin(v) + 1; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.push_back(1); // expected-note {{invalidated here}}
|
|
(void)*it; // expected-note {{later used here}}
|
|
}
|
|
} // namespace SimpleInvalidIterators
|
|
|
|
namespace InvalidatingThroughContainerAliases {
|
|
void IteratorInvalidatedThroughLocalReferenceAlias() {
|
|
std::vector<int> vv;
|
|
std::vector<int> &v = vv;
|
|
auto it = vv.begin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.push_back(42); // expected-note {{invalidated here}}
|
|
(void)it; // expected-note {{later used here}}
|
|
}
|
|
|
|
void IteratorInvalidatedThroughPointerParameter(std::vector<int> *v) { // expected-warning {{parameter is later invalidated}}
|
|
auto it = v->begin();
|
|
v->push_back(42); // expected-note {{invalidated here}}
|
|
(void)it; // expected-note {{later used here}}
|
|
}
|
|
|
|
void ParenthesizedContainerInvalidatesIterator() {
|
|
// FIXME: Support invalidation through non-DRE lvalue expressions.
|
|
std::vector<int> v;
|
|
auto it = v.begin();
|
|
(v).push_back(42);
|
|
(void)it;
|
|
}
|
|
|
|
} // namespace InvalidatingThroughContainerAliases
|
|
|
|
namespace ContainerObjectAliases {
|
|
// FIXME: Distinguish owner-borrow from content-borrow.
|
|
void PointerParameterObjectUseIsOk(std::vector<int> *v) { // expected-warning {{parameter is later invalidated}}
|
|
v->push_back(42); // expected-note {{invalidated here}}
|
|
(void)v; // expected-note {{later used here}}
|
|
}
|
|
|
|
// FIXME: Distinguish owner-borrow from content-borrow.
|
|
void LocalPointerAliasObjectUseIsOk() {
|
|
std::vector<int> vv;
|
|
std::vector<int> *v = &vv; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v->push_back(42); // expected-note {{invalidated here}}
|
|
(void)*v; // expected-note {{later used here}}
|
|
}
|
|
|
|
// FIXME: Distinguish owner-borrow from content-borrow.
|
|
void LocalReferenceAliasObjectUseIsOk() {
|
|
std::vector<int> vv;
|
|
std::vector<int> &v = vv; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.push_back(42); // expected-note {{invalidated here}}
|
|
(void)v; // expected-note {{later used here}}
|
|
}
|
|
} // namespace ContainerObjectAliases
|
|
|
|
namespace ElementReferences {
|
|
// Testing raw pointers and references to elements, not just iterators.
|
|
|
|
void ReferenceToVectorElement() {
|
|
std::vector<int> v = {1, 2, 3};
|
|
int& ref = v[0]; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.push_back(4); // expected-note {{invalidated here}}
|
|
ref = 10; // expected-note {{later used here}}
|
|
(void)ref;
|
|
}
|
|
|
|
void PointerRefToVectorElement() {
|
|
std::vector<int*> v = {nullptr, nullptr};
|
|
int*& ref = v[0]; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.push_back(nullptr); // expected-note {{invalidated here}}
|
|
ref = nullptr; // expected-note {{later used here}}
|
|
}
|
|
|
|
void PointerToVectorElement() {
|
|
std::vector<int> v = {1, 2, 3};
|
|
int* ptr = &v[0]; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
v.resize(100); // expected-note {{invalidated here}}
|
|
*ptr = 10; // expected-note {{later used here}}
|
|
}
|
|
|
|
void SelfInvalidatingMap() {
|
|
std::flat_map<int, std::string> mp;
|
|
// TODO: We do not have a way to differentiate between pointer stability and iterator stability!
|
|
//
|
|
// std::unordered_map and other node-based containers provide pointer/reference stability.
|
|
// Therefore the following is safe in practice.
|
|
// On the other hand, std::flat_map (since C++23) does not provide pointer stability on
|
|
// insertion and following is unsafe for this container.
|
|
mp[1] = "42";
|
|
mp[2] // expected-note {{invalidated here}}
|
|
=
|
|
mp[1]; // expected-warning {{object whose reference is captured is later invalidated}} 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 {{object whose reference is captured is later invalidated}}
|
|
mp.erase(mp.find(4)); // expected-note {{invalidated here}}
|
|
if (it != mp.end()) // expected-note {{later used here}}
|
|
*it;
|
|
}
|
|
} // namespace ElementReferences
|
|
|
|
namespace Strings {
|
|
|
|
void append(std::string str) {
|
|
std::string_view view = str; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
str += "456"; // expected-note {{invalidated here}}
|
|
(void)view; // expected-note {{later used here}}
|
|
}
|
|
void reassign(std::string str, std::string str2) {
|
|
std::string_view view = str; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
str = str2; // expected-note {{invalidated here}}
|
|
(void)view; // expected-note {{later used here}}
|
|
}
|
|
} // namespace Strings
|
|
|
|
// FIXME: This should be diagnosed as use-after-invalidation but with potential move.
|
|
void ReassigningAfterMove(std::string str, std::string str2) {
|
|
std::string_view view = str; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
std::vector<std::string> someStorage;
|
|
someStorage.push_back(std::move(str));
|
|
str = str2; // expected-note {{invalidated here}}
|
|
(void)view; // expected-note {{later used here}}
|
|
}
|
|
|
|
namespace ContainersAsFields {
|
|
struct S {
|
|
std::vector<std::string> strings1;
|
|
std::vector<std::string> strings2;
|
|
};
|
|
// FIXME: Make Paths more precise to reason at field granularity.
|
|
// Currently we only detect invalidations to direct declarations and not members.
|
|
void Invalidate1Use1IsInvalid() {
|
|
// FIXME: Detect this.
|
|
S s;
|
|
auto it = s.strings1.begin();
|
|
s.strings1.push_back("1");
|
|
*it;
|
|
}
|
|
void Invalidate2Use1IsOk() {
|
|
S s;
|
|
auto it = s.strings1.begin();
|
|
s.strings2.push_back("1");
|
|
*it;
|
|
}
|
|
void ConditionalContainerInvalidatesIterator(bool flag) {
|
|
// FIXME: Support invalidation through conditional lvalue expressions.
|
|
std::vector<int> v1, v2;
|
|
auto it = v1.begin();
|
|
(flag ? v1 : v2).push_back(42);
|
|
(void)it;
|
|
}
|
|
void ConditionalFieldInvalidatesIterator(bool flag) {
|
|
// FIXME: Support conditional invalidation through field expressions.
|
|
S s;
|
|
auto it = s.strings1.begin();
|
|
(flag ? s.strings1 : s.strings2).push_back("1");
|
|
*it;
|
|
}
|
|
// FIXME: Requires field-sensitive AccessPaths to fix.
|
|
void Invalidate1Use2ViaRefIsOk() {
|
|
S s;
|
|
auto it = s.strings2.begin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto& strings1 = s.strings1;
|
|
strings1.push_back("1"); // expected-note {{invalidated here}}
|
|
*it; // expected-note {{later used here}}
|
|
}
|
|
void Invalidate1UseSIsOk() {
|
|
S s;
|
|
S* p = &s;
|
|
s.strings2.push_back("1");
|
|
(void)*p;
|
|
}
|
|
// FIXME: Distinguish owner-borrow from content-borrow.
|
|
void PointerToContainerIsOk() {
|
|
std::vector<std::string> s;
|
|
std::vector<std::string>* p = &s; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
p->push_back("1"); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
void IteratorFromPointerToContainerIsInvalidated() {
|
|
std::vector<std::string> s;
|
|
std::vector<std::string>* p = &s; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto it = p->begin();
|
|
p->push_back("1"); // expected-note {{invalidated here}}
|
|
*it; // expected-note {{later used here}}
|
|
}
|
|
// FIXME: Distinguish invalidating an element's contents from invalidating
|
|
// iterators into the outer container.
|
|
void ChangingRegionOwnedByContainerIsOk() {
|
|
std::vector<std::string> subdirs;
|
|
for (std::string& path : subdirs) // expected-warning {{object whose reference is captured is later invalidated}} expected-note {{later used here}}
|
|
path = std::string(); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
} // namespace ContainersAsFields
|
|
|
|
namespace InvalidatedField {
|
|
std::string StableString;
|
|
|
|
// FIXME: Distinguish owner-borrow from interior-borrow.
|
|
struct SinkOwnerBorrow {
|
|
std::string *dest_; // expected-note {{this field dangles}}
|
|
|
|
SinkOwnerBorrow(std::string *dest, int n) : dest_(dest) { // expected-warning {{parameter which escapes to a field is later invalidated}}
|
|
if (n > 0)
|
|
dest->clear(); // expected-note {{invalidated here}}
|
|
}
|
|
};
|
|
|
|
struct SinkInteriorBorrow {
|
|
const char *dest_; // expected-note {{this field dangles}}
|
|
|
|
SinkInteriorBorrow(std::string *dest, int n) : dest_(dest->data()) { // expected-warning {{parameter which escapes to a field is later invalidated}}
|
|
if (n > 0)
|
|
dest->clear(); // expected-note {{invalidated here}}
|
|
}
|
|
};
|
|
|
|
struct S {
|
|
std::string_view FieldFromLocalVector; // expected-note {{this field dangles}}
|
|
std::string_view FieldFromByValueParamVector; // expected-note {{this field dangles}}
|
|
std::string_view FieldFromLocalString; // expected-note {{this field dangles}}
|
|
std::string_view FieldFromByValueParamString; // expected-note {{this field dangles}}
|
|
std::string_view FieldFromRefParamString; // expected-note {{this field dangles}}
|
|
int *FieldFromNew; // expected-note {{this field dangles}}
|
|
int *FieldFromPointerParam; // expected-note {{this field dangles}}
|
|
std::string_view FieldReassigned;
|
|
|
|
void InvalidatedFieldLocalVector() {
|
|
std::vector<std::string> strings;
|
|
FieldFromLocalVector = *strings.begin(); // expected-warning {{object whose reference escapes to a field is later invalidated}}
|
|
strings.push_back("1"); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void InvalidatedFieldByValueParamVector(std::vector<std::string> strings) {
|
|
FieldFromByValueParamVector = *strings.begin(); // expected-warning {{object whose reference escapes to a field is later invalidated}}
|
|
strings.push_back("1"); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void InvalidatedFieldLocalString() {
|
|
std::string s;
|
|
FieldFromLocalString = s; // expected-warning {{object whose reference escapes to a field is later invalidated}}
|
|
s.clear(); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void InvalidatedFieldByValueParamString(std::string s) {
|
|
FieldFromByValueParamString = s; // expected-warning {{object whose reference escapes to a field is later invalidated}}
|
|
s.clear(); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void InvalidatedFieldRefParamString(std::string &s) { // expected-warning {{parameter which escapes to a field is later invalidated}}
|
|
FieldFromRefParamString = s;
|
|
s.~basic_string(); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void InvalidatedFieldDelete() {
|
|
int *p = new int; // expected-warning {{object whose reference escapes to a field is later invalidated}}
|
|
FieldFromNew = p;
|
|
delete p; // expected-note {{freed here}}
|
|
}
|
|
|
|
void InvalidatedFieldDeleteParam(int *p) { // expected-warning {{parameter which escapes to a field is later invalidated}}
|
|
FieldFromPointerParam = p;
|
|
delete p; // expected-note {{freed here}}
|
|
}
|
|
|
|
void FieldReassignedBeforeInvalidation() {
|
|
std::vector<std::string> strings;
|
|
FieldReassigned = *strings.begin();
|
|
FieldReassigned = StableString;
|
|
strings.push_back("1");
|
|
}
|
|
};
|
|
} // namespace InvalidatedField
|
|
|
|
namespace InvalidatedGlobal {
|
|
std::string StableString;
|
|
std::string_view GlobalFromLocalVector; // expected-note {{this global dangles}}
|
|
std::string_view GlobalFromByValueParamString; // expected-note {{this global dangles}}
|
|
std::string_view GlobalFromRefParamString; // expected-note {{this global dangles}}
|
|
int *GlobalFromNew; // expected-note {{this global dangles}}
|
|
int *GlobalFromPointerParam; // expected-note {{this global dangles}}
|
|
std::string_view GlobalReassigned;
|
|
|
|
struct S {
|
|
static std::string_view StaticMember; // expected-note {{this static storage dangles}}
|
|
};
|
|
|
|
void InvalidatedGlobalLocalVector() {
|
|
std::vector<std::string> strings;
|
|
GlobalFromLocalVector = *strings.begin(); // expected-warning {{object whose reference escapes to global or static storage is later invalidated}}
|
|
strings.push_back("1"); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void InvalidatedGlobalByValueParamString(std::string s) {
|
|
GlobalFromByValueParamString = s; // expected-warning {{object whose reference escapes to global or static storage is later invalidated}}
|
|
s.clear(); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void InvalidatedGlobalRefParamString(std::string &s) { // expected-warning {{parameter which escapes to global or static storage is later invalidated}}
|
|
GlobalFromRefParamString = s;
|
|
s.~basic_string(); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void InvalidatedGlobalDelete() {
|
|
int *p = new int; // expected-warning {{object whose reference escapes to global or static storage is later invalidated}}
|
|
GlobalFromNew = p;
|
|
delete p; // expected-note {{freed here}}
|
|
}
|
|
|
|
void InvalidatedGlobalDeleteParam(int *p) { // expected-warning {{parameter which escapes to global or static storage is later invalidated}}
|
|
GlobalFromPointerParam = p;
|
|
delete p; // expected-note {{freed here}}
|
|
}
|
|
|
|
void InvalidatedStaticLocalString() {
|
|
static std::string_view StaticFromLocalString; // expected-note {{this static storage dangles}}
|
|
std::string s;
|
|
StaticFromLocalString = s; // expected-warning {{object whose reference escapes to global or static storage is later invalidated}}
|
|
s.clear(); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void InvalidatedStaticMemberString() {
|
|
std::string s;
|
|
S::StaticMember = s; // expected-warning {{object whose reference escapes to global or static storage is later invalidated}}
|
|
s.clear(); // expected-note {{invalidated here}}
|
|
}
|
|
|
|
void GlobalReassignedBeforeInvalidation() {
|
|
std::vector<std::string> strings;
|
|
GlobalReassigned = *strings.begin();
|
|
GlobalReassigned = StableString;
|
|
strings.push_back("1");
|
|
}
|
|
} // namespace InvalidatedGlobal
|
|
|
|
namespace AssociativeContainers {
|
|
void SetInsertDoesNotInvalidate() {
|
|
std::set<int> s;
|
|
s.insert(0);
|
|
auto it = s.begin();
|
|
s.insert(2);
|
|
*it;
|
|
}
|
|
|
|
void MapInsertDoesNotInvalidate() {
|
|
std::map<int, int> m;
|
|
auto it = m.begin();
|
|
m.insert({1, 2});
|
|
*it;
|
|
}
|
|
|
|
void MapEmplaceDoesNotInvalidate() {
|
|
std::map<int, int> m;
|
|
auto it = m.begin();
|
|
m.emplace(1, 2);
|
|
*it;
|
|
}
|
|
|
|
void MultisetInsertDoesNotInvalidate() {
|
|
std::multiset<int> s;
|
|
auto it = s.begin();
|
|
s.insert(1);
|
|
*it;
|
|
}
|
|
|
|
void MultimapInsertDoesNotInvalidate() {
|
|
std::multimap<int, int> m;
|
|
auto it = m.begin();
|
|
m.insert({1, 2});
|
|
*it;
|
|
}
|
|
|
|
void SetEraseDoesNotInvalidateOthers() {
|
|
std::set<int> s;
|
|
s.insert(1);
|
|
s.insert(2);
|
|
auto it1 = s.begin();
|
|
auto it2 = it1;
|
|
++it2;
|
|
s.erase(it2);
|
|
*it1;
|
|
}
|
|
|
|
void SetExtractDoesNotInvalidateOthers() {
|
|
std::set<int> s;
|
|
s.insert(1);
|
|
s.insert(2);
|
|
auto it1 = s.begin();
|
|
auto it2 = it1;
|
|
++it2;
|
|
s.extract(it2);
|
|
*it1;
|
|
}
|
|
|
|
void SetClearInvalidates() {
|
|
std::set<int> s;
|
|
auto it = s.begin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
s.clear(); // expected-note {{invalidated here}}
|
|
*it; // expected-note {{later used here}}
|
|
}
|
|
|
|
void MapClearInvalidates() {
|
|
std::map<int, int> m;
|
|
auto it = m.begin(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
m.clear(); // expected-note {{invalidated here}}
|
|
*it; // expected-note {{later used here}}
|
|
}
|
|
|
|
void MapSubscriptDoesNotInvalidate() {
|
|
std::map<int, int> m;
|
|
auto it = m.begin();
|
|
m[1];
|
|
*it;
|
|
}
|
|
|
|
void PrintMax(const int& a, const int& b);
|
|
|
|
void MapSubscriptMultipleCallsDoesNotInvalidate(std::map<int, int> mp, int a, int b) {
|
|
PrintMax(mp[a], mp[b]);
|
|
}
|
|
|
|
void FlatMapSubscriptMultipleCallsInvalidate(std::flat_map<int, int> mp, int a, int b) {
|
|
PrintMax(mp[a], mp[b]); // expected-warning {{object whose reference is captured is later invalidated}} \
|
|
// expected-note {{invalidated here}} \
|
|
// expected-note {{later used here}}
|
|
}
|
|
|
|
} // namespace AssociativeContainers
|
|
|
|
namespace lambda_capture_invalidation {
|
|
void captured_view_invalidated_by_owner() {
|
|
std::string s = "42";
|
|
std::string_view p = s; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto lambda = [=]() { return p; };
|
|
s.push_back('c'); // expected-note {{invalidated here}}
|
|
lambda(); // expected-note {{later used here}}
|
|
}
|
|
|
|
void multiple_captures_one_invalidated() {
|
|
std::string s1 = "a", s2 = "b";
|
|
std::string_view p1 = s1, p2 = s2; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
auto lambda = [=]() { return p1.size() + p2.size(); };
|
|
s1.clear(); // expected-note {{invalidated here}}
|
|
lambda(); // expected-note {{later used here}}
|
|
}
|
|
|
|
// FIXME: By-ref captures flow only the outermost origin, so
|
|
// invalidation of the captured view's pointee is not propagated.
|
|
void ref_capture_owner_invalidated() {
|
|
std::string s = "42";
|
|
std::string_view p = s;
|
|
auto lambda = [&]() { return p; };
|
|
s.push_back('c'); // invalidates p
|
|
lambda(); // should warn: use-after-invalidate
|
|
}
|
|
|
|
// FIXME: Once inner origins are tracked, this case must remain a no-warning.
|
|
// Reassigning `p` through the by-ref capture should invalidate the link to `s`.
|
|
void ref_capture_reassigned_to_safe() {
|
|
std::string s = "42", safe = "not modified";
|
|
std::string_view p = s;
|
|
auto lambda = [&]() { return p; };
|
|
p = safe; // p now points to 'safe', not 's'
|
|
s.push_back('c'); // does not invalidate p anymore
|
|
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
|
|
|
|
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 {{object whose reference is captured is later invalidated}}
|
|
v.push_back(2); // expected-note {{invalidated here}}
|
|
(void)f; // expected-note {{later used here}}
|
|
}
|
|
|
|
} // namespace callable_wrappers
|
|
|
|
// FIXME: does not report a double free
|
|
namespace explicit_destructor {
|
|
|
|
void explicit_destructor_invalidates_pointer() {
|
|
std::string s = "42";
|
|
const char *p = s.data(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
s.~basic_string(); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
void pointer_destructor_invalidates_pointer() {
|
|
char storage[sizeof(std::string)];
|
|
std::string *obj = new (storage) std::string("42"); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
const char *p = obj->data();
|
|
obj->~basic_string(); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
void destroy_at_invalidates_pointer() {
|
|
char storage[sizeof(std::string)];
|
|
std::string *obj = new (storage) std::string("42"); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
const char *p = obj->data();
|
|
std::destroy_at(obj); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
void destroy_at_then_placement_new_rescues_pointer() {
|
|
char storage[sizeof(std::string)];
|
|
std::string *obj = new (storage) std::string("42");
|
|
const char *p = obj->data();
|
|
std::destroy_at(obj);
|
|
obj = new (storage) std::string("23");
|
|
p = obj->data();
|
|
(void)*p;
|
|
}
|
|
|
|
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 {{object whose reference is captured is later invalidated}}
|
|
std::destroy_at(&arr_ref); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
void reference_destructor_invalidates_pointer() {
|
|
std::string s = "42";
|
|
std::string &ref = s; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
const char *p = ref.data();
|
|
std::destroy_at(&ref); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
void destroy_at_ternary_operator(bool flag) {
|
|
std::string* str1 = new std::string; // expected-warning {{object whose reference is captured is later invalidated}}
|
|
std::string* str2 = new std::string;
|
|
const char *p = str1->data();
|
|
std::destroy_at(flag ? str1 : str2); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
struct StringOwner {
|
|
std::string s, t;
|
|
};
|
|
|
|
// FIXME: False-positive
|
|
void member_destructor_invalidates_pointer() {
|
|
StringOwner owner = {"42", "43"};
|
|
const char *p = owner.s.data(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
owner.t.~basic_string(); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
} // namespace explicit_destructor
|
|
|
|
namespace unique_ptr_invalidation {
|
|
|
|
void invalid_after_reset() {
|
|
std::unique_ptr<int> up(new int);
|
|
int *p = up.get(); // expected-warning {{object whose reference is captured is later invalidated}}
|
|
up.reset(); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
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 {{object whose reference is captured is later invalidated}}
|
|
up = std::move(other); // expected-note {{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 {{object whose reference is captured is later invalidated}}
|
|
up = nullptr; // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
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 {{object whose reference is captured is later invalidated}}
|
|
up.reset(); // expected-note {{invalidated here}}
|
|
(void)*p; // expected-note {{later used here}}
|
|
}
|
|
|
|
} // namespace unique_ptr_invalidation
|