llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: PushkarSingh (iitianpushkar)

<details>
<summary>Changes</summary>

This updates Lifetime Safety alias-chain diagnostics to identify overloaded 
operator and conversion calls instead of describing them generically as an 
`expression`.

Previously, these calls were reported as `expression aliases the storage of 
...`, which could make it unclear which operation introduced the alias. The 
diagnostic now reports the callee using the existing diagnostic name printer, 
producing descriptions such as:

- `result of call to 'operator-&gt;'`
- `result of call to 'operator()'`
- `result of call to 'operator basic_string_view'`

Template arguments are preserved for operator function-template 
specializations. For example:

- `result of call to 'operator+&lt;const int&gt;'`
- `result of call to 'operator-&lt;__gnu_cxx::basic_iterator&lt;const 
int&gt;&gt;'`

This makes each aliasing step explicit and identifies the particular call 
responsible for propagating the alias.

References to [#<!-- 
-->220248#discussion_r3905817437](https://github.com/llvm/llvm-project/pull/220248#discussion_r3905817437)

---

Patch is 33.79 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/221803.diff


4 Files Affected:

- (modified) clang/lib/Sema/SemaLifetimeSafety.h (-2) 
- (modified) clang/test/Sema/LifetimeSafety/invalidations.cpp (+18-16) 
- (modified) clang/test/Sema/LifetimeSafety/nocfg.cpp (+14-14) 
- (modified) clang/test/Sema/LifetimeSafety/safety.cpp (+14-14) 


``````````diff
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 0fd3486bcee46..620032c27f955 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -687,8 +687,6 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
       const auto *FD = CE->getDirectCallee();
       if (!FD)
         return "result of call";
-      if (FD->isOverloadedOperator() || isa<CXXConversionDecl>(FD))
-        return "expression";
       std::string Name;
       llvm::raw_string_ostream OS(Name);
       FD->getNameForDiagnostic(OS, S.getPrintingPolicy(),
diff --git a/clang/test/Sema/LifetimeSafety/invalidations.cpp 
b/clang/test/Sema/LifetimeSafety/invalidations.cpp
index 127e375bc023c..593ba92cf7443 100644
--- a/clang/test/Sema/LifetimeSafety/invalidations.cpp
+++ b/clang/test/Sema/LifetimeSafety/invalidations.cpp
@@ -271,7 +271,7 @@ void IteratorUsedAfterPreIncrement() {
   std::vector<int> v;
   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'}}
+  auto next = ++it;         // expected-note {{result of call to 'operator++' 
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}}
 }
@@ -279,7 +279,7 @@ void IteratorUsedAfterPreIncrement() {
 void IteratorUsedAfterPostDecrement(std::vector<int> v) {
   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'}}
+  auto prev = it--;         // expected-note {{result of call to 'operator--' 
aliases the storage of parameter 'v'}}
   v.push_back(1);           // expected-note {{parameter 'v' is invalidated 
here}}
   (void)*prev;              // expected-note {{later used here}}
 }
@@ -288,7 +288,7 @@ void IteratorUsedAfterAddition() {
   std::vector<int> v;
   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'}}
+  auto next = it + 5;       // expected-note {{result of call to 'operator+' 
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}}
 }
@@ -297,7 +297,7 @@ void IteratorUsedAfterReverseSubtraction(std::vector<int> 
v) {
   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'}}
+                            // expected-note {{result of call to 
'operator-<__gnu_cxx::basic_iterator<const int>>' aliases the storage of 
parameter 'v'}}
   v.push_back(1);           // expected-note {{parameter 'v' is invalidated 
here}}
   (void)*prev;              // expected-note {{later used here}}
 }
@@ -305,7 +305,7 @@ void IteratorUsedAfterReverseSubtraction(std::vector<int> 
v) {
 void IteratorUsedAfterAddAdd(std::vector<int> v) {
   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'}}
+  auto next = (it + 5) + 5; // expected-note 2 {{result of call to 'operator+' 
aliases the storage of parameter 'v'}}
   v.push_back(1);           // expected-note {{parameter 'v' is invalidated 
here}}
   (void)*next;              // expected-note {{later used here}}
 }
@@ -314,7 +314,8 @@ void IteratorUsedAfterMixedAddition() {
   std::vector<int> v;
   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'}} \
+  auto next = 1 + it + 2 + 3;   // expected-note {{result of call to 
'operator+<const int>' aliases the storage of local variable 'v'}} \
+                                // expected-note 2 {{result of call to 
'operator+' 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}}
@@ -323,7 +324,8 @@ void IteratorUsedAfterMixedAddition() {
 void IteratorUsedAfterPreIncrementAddAssign(std::vector<int> v) {
   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'}}
+  it = ++it + 1 + 2;            // expected-note {{result of call to 
'operator++' aliases the storage of parameter 'v'}} \
+                                // expected-note 2 {{result of call to 
'operator+' aliases the storage of parameter 'v'}}
   v.push_back(1);               // expected-note {{parameter 'v' is 
invalidated here}}
   (void)*it;                    // expected-note {{later used here}}
 }
@@ -331,7 +333,7 @@ void 
IteratorUsedAfterPreIncrementAddAssign(std::vector<int> v) {
 void IteratorUsedAfterBeginAddAssign() {
   std::vector<int> v;
   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 
'operator+' 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}}
@@ -341,7 +343,7 @@ void IteratorUsedAfterStdBeginAddAssign() {
   std::vector<int> v;
   std::vector<int>::iterator it;
   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 
'operator+' 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}}
@@ -404,7 +406,7 @@ namespace ElementReferences {
 void ReferenceToVectorElement() {
   std::vector<int> v = {1, 2, 3};
   int& ref = v[0]; // expected-warning {{local variable 'v' is later 
invalidated}} \
-                   // expected-note {{expression aliases the storage of local 
variable 'v'}}
+                   // expected-note {{result of call to 'operator[]' 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;
@@ -413,7 +415,7 @@ void ReferenceToVectorElement() {
 void PointerRefToVectorElement() {
   std::vector<int*> v = {nullptr, nullptr};
   int*& ref = v[0];     // expected-warning {{local variable 'v' is later 
invalidated}} \
-                        // expected-note {{expression aliases the storage of 
local variable 'v'}}
+                        // expected-note {{result of call to 'operator[]' 
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}}
 }
@@ -421,7 +423,7 @@ void PointerRefToVectorElement() {
 void PointerToVectorElement() {
   std::vector<int> v = {1, 2, 3};
   int* ptr = &v[0];  // expected-warning {{local variable 'v' is later 
invalidated}} \
-                     // expected-note {{expression aliases the storage of 
local variable 'v'}}
+                     // expected-note {{result of call to 'operator[]' 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}}
 }
@@ -447,7 +449,7 @@ 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 {{result of call to 'operator[]' aliases the 
storage of local variable 'mp'}} \
                  // expected-note {{later used here}}
 }
 
@@ -798,7 +800,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 2 {{result of call to 
'operator[]' aliases the storage of parameter 'mp'}} \
                             // expected-note {{later used here}}
 }
 
@@ -851,7 +853,7 @@ struct S {
   void baz(){
     std::vector<std::string> vec = {"42"};
     v = vec[0];         // expected-warning {{local variable 'vec' is later 
invalidated}} \
-                        // expected-note {{expression aliases the storage of 
local variable 'vec'}}
+                        // expected-note {{result of call to 'operator[]' 
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;
@@ -865,7 +867,7 @@ 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}} \
-                                                        // expected-note 
{{expression aliases the storage of local variable 'v'}}
+                                                        // expected-note 
{{result of call to 'operator[]' 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}}
 }
diff --git a/clang/test/Sema/LifetimeSafety/nocfg.cpp 
b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index 5c5f0e6f7ce52..7ef209572c437 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp
@@ -310,11 +310,11 @@ std::string_view danglingRefToOptionalFromTemp4() {
 void danglingReferenceFromTempOwner() {
   int &&r = *std::optional<int>();          // expected-warning {{object 
backing the pointer will be destroyed at the end of the full-expression}} \
                                             // cfg-warning {{temporary object 
does not live long enough}} cfg-note {{destroyed here}} \
-                                            // cfg-note {{expression aliases 
the storage of temporary object because the implicit object parameter is 
inferred as lifetimebound}}
+                                            // cfg-note {{result of call to 
'operator*' aliases the storage of temporary object because the implicit object 
parameter is inferred as lifetimebound}}
   // https://github.com/llvm/llvm-project/issues/175893
   int &&r2 = *std::optional<int>(5);        // expected-warning {{object 
backing the pointer will be destroyed at the end of the full-expression}} \
                                               // cfg-warning {{temporary 
object does not live long enough}} cfg-note {{destroyed here}} \
-                                              // cfg-note {{expression aliases 
the storage of temporary object because the implicit object parameter is 
inferred as lifetimebound}}
+                                              // cfg-note {{result of call to 
'operator*' aliases the storage of temporary object because the implicit object 
parameter is inferred as lifetimebound}}
 
   // https://github.com/llvm/llvm-project/issues/175893
   int &&r3 = std::optional<int>(5).value(); // expected-warning {{object 
backing the pointer will be destroyed at the end of the full-expression}} \
@@ -331,7 +331,7 @@ void danglingReferenceFromTempOwner() {
 
   std::string_view sv = *getTempOptStr();  // expected-warning {{object 
backing the pointer will be destroyed at the end of the full-expression}} \
                                            // cfg-warning {{temporary object 
does not live long enough}} cfg-note {{destroyed here}} \
-                                           // cfg-note {{expression aliases 
the storage of temporary object because the implicit object parameter is 
inferred as lifetimebound}}
+                                           // cfg-note {{result of call to 
'operator*' aliases the storage of temporary object because the implicit object 
parameter is inferred as lifetimebound}}
   use(sv);                                 // cfg-note {{later used here}}
 }
 
@@ -343,7 +343,7 @@ void testLoops() {
     ;
   for (auto i : *getTempOptVec()) // expected-warning {{object backing the 
pointer will be destroyed at the end of the full-expression}} \
                                   // cfg-warning {{temporary object does not 
live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}} \
-                                  // cfg-note {{expression aliases the storage 
of temporary object because the implicit object parameter is inferred as 
lifetimebound}}
+                                  // cfg-note {{result of call to 'operator*' 
aliases the storage of temporary object because the implicit object parameter 
is inferred as lifetimebound}}
     ;
 }
 
@@ -1050,15 +1050,15 @@ void operator_star_arrow_reference() {
   auto temporary = []() { return std::vector<std::string>{{"1"}}; };
   const char* x = temporary().begin()->data();    // cfg-warning {{temporary 
object does not live long enough}} cfg-note {{destroyed here}} \
                                                   // cfg-note {{result of call 
to 'begin' aliases the storage of temporary object because the implicit object 
parameter is inferred as lifetimebound}} \
-  // cfg-note {{expression aliases the storage of temporary object because the 
implicit object parameter is inferred as lifetimebound}} \
+  // cfg-note {{result of call to 'operator->' aliases the storage of 
temporary object because the implicit object parameter is inferred as 
lifetimebound}} \
   // cfg-note {{result of call to 'data' aliases the storage of temporary 
object because the implicit object parameter is inferred as lifetimebound}}
   const char* y = (*temporary().begin()).data();  // cfg-warning {{temporary 
object does not live long enough}} cfg-note {{destroyed here}} \
                                                   // cfg-note {{result of call 
to 'begin' aliases the storage of temporary object because the implicit object 
parameter is inferred as lifetimebound}} \
-  // cfg-note {{expression aliases the storage of temporary object because the 
implicit object parameter is inferred as lifetimebound}} \
+  // cfg-note {{result of call to 'operator*' aliases the storage of temporary 
object because the implicit object parameter is inferred as lifetimebound}} \
   // cfg-note {{result of call to 'data' aliases the storage of temporary 
object because the implicit object parameter is inferred as lifetimebound}}
   const std::string& z = (*temporary().begin());  // cfg-warning {{temporary 
object does not live long enough}} cfg-note {{destroyed here}} \
                                                   // cfg-note {{result of call 
to 'begin' aliases the storage of temporary object because the implicit object 
parameter is inferred as lifetimebound}} \
-                                                   // cfg-note {{expression 
aliases the storage of temporary object because the implicit object parameter 
is inferred as lifetimebound}}
+                                                   // cfg-note {{result of 
call to 'operator*' aliases the storage of temporary object because the 
implicit object parameter is inferred as lifetimebound}}
 
   use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
 }
@@ -1072,15 +1072,15 @@ void 
operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() {
   auto temporary = []() { return std::vector<std::pair<int, std::string>>{{1, 
"1"}}; };
   const char* x = temporary().begin()->second.data();   // cfg-warning 
{{temporary object does not live long enough}} cfg-note {{destroyed here}} \
                                                         // cfg-note {{result 
of call to 'begin' aliases the storage of temporary object because the implicit 
object parameter is inferred as lifetimebound}} \
-                                                        // cfg-note 
{{expression aliases the storage of temporary object because the implicit 
object parameter is inferred as lifetimebound}} \
+                                                        // cfg-note {{result 
of call to 'operator->' aliases the storage of temporary object because the 
implicit object parameter is inferred as lifetimebound}} \
                                                         // cfg-note {{result 
of call to 'data' aliases the storage of temporary object because the implicit 
object parameter is inferred as lifetimebound}}
   const char* y = (*temporary().begin()).second.data(); // cfg-warning 
{{temporary object does not live long enough}} cfg-note {{destroyed here}} \
                                                         // cfg-note {{result 
of call to 'begin' aliases the storage of temporary object because the implicit 
object parameter is inferred as lifetimebound}} \
-                                                        // cfg-note 
{{expression aliases the storage of temporary object because the implicit 
object parameter is inferred as lifetimebound}} \
+                                                        // cfg-note {{result 
of call to 'operator*' aliases the storage of temporary object because the 
implicit object parameter is inferred as lifetimebound}} \
                                                         // cfg-note {{result 
of call to 'data' aliases the storage of temporary object because the implicit 
object parameter is inferred as lifetimebound}}
   const std::string& z = (*temporary().begin()).second; // cfg-warning 
{{temporary object does not live long enough}} cfg-note {{destroyed here}} \
                                                        // cfg-note {{result of 
call to 'begin' aliases the storage of temporary object because the implicit 
object parameter is inferred as lifetimebound}} \
-                                                        // cfg-note 
{{expression aliases the storage of tem...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/221803
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to