https://github.com/benedekaibas updated 
https://github.com/llvm/llvm-project/pull/215409

>From c2cd1ff1c6cee173fd9381a353b288fc77481aaa Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Mon, 10 Aug 2026 23:43:34 +0200
Subject: [PATCH 1/4] [analyzer] Only report the first dereference of the same
 variable in DanglingPtrDeref

---
 .../StaticAnalyzer/Checkers/DanglingPtrDeref.cpp    |  6 ++++++
 .../StaticAnalyzer/Checkers/LifetimeModeling.cpp    |  8 ++++++++
 .../lib/StaticAnalyzer/Checkers/LifetimeModeling.h  |  5 +++++
 clang/test/Analysis/dangling-ptr-deref.cpp          | 13 +++++++++++++
 4 files changed, 32 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp 
b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
index bd4cd864cb768..cd690328ebc47 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
@@ -69,6 +69,11 @@ void DanglingPtrDeref::checkPostCall(const CallEvent &Call,
 void DanglingPtrDeref::reportUseAfterScope(const MemRegion *Region,
                                            const Stmt *S, ExplodedNode *N,
                                            CheckerContext &C) const {
+  ProgramStateRef ReportedState =
+      lifetime_modeling::markAsReported(N->getState(), Region);
+  if (!ReportedState)
+    return;
+
   auto BR = std::make_unique<PathSensitiveBugReport>(
       BugMsg,
       (llvm::Twine("Use of ") + lifetime_modeling::getRegionName(Region) +
@@ -79,6 +84,7 @@ void DanglingPtrDeref::reportUseAfterScope(const MemRegion 
*Region,
     if (const Expr *DerefExpr = bugreporter::getDerefExpr(S))
       bugreporter::trackExpressionValue(N, DerefExpr, *BR);
   }
+  C.addTransition(ReportedState, N);
   C.emitReport(std::move(BR));
 }
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 2fab20b199f01..d4a3cfd060aa3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -15,6 +15,7 @@ REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, 
const MemRegion *)
 REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet)
 
 REGISTER_SET_WITH_PROGRAMSTATE(DeallocatedSourceSet, const MemRegion *)
+REGISTER_SET_WITH_PROGRAMSTATE(ReportedDeadRegions, const MemRegion *)
 
 namespace {
 
@@ -86,6 +87,13 @@ bool lifetime_modeling::isDeallocated(ProgramStateRef State,
   return State->contains<DeallocatedSourceSet>(Region->getBaseRegion());
 }
 
+ProgramStateRef lifetime_modeling::markAsReported(ProgramStateRef State,
+                                                  const MemRegion *Region) {
+  if (State->contains<ReportedDeadRegions>(Region->getBaseRegion()))
+    return nullptr;
+  return State->add<ReportedDeadRegions>(Region->getBaseRegion());
+}
+
 static ProgramStateRef bindSource(ProgramStateRef State, SVal RetVal,
                                   const MemRegion *Source) {
   LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
index 8d6c8e4882d1c..8cfe4526d0d51 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
@@ -22,6 +22,11 @@ bool isBoundToLifetimeSource(ProgramStateRef State, SVal 
Val);
 /// Returns the descriptive name of the memory region or a placeholder if a
 /// descriptive name cannot be constructed for it.
 std::string getRegionName(const MemRegion *Reg);
+
+/// Returns true if \p R is seen the first time. If R was alreay reported
+/// before returns false.
+ProgramStateRef markAsReported(ProgramStateRef State, const MemRegion *Region);
+
 } // namespace clang::ento::lifetime_modeling
 
 #endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H
diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp 
b/clang/test/Analysis/dangling-ptr-deref.cpp
index 7f13c241dadf0..58f4aa7fa7583 100644
--- a/clang/test/Analysis/dangling-ptr-deref.cpp
+++ b/clang/test/Analysis/dangling-ptr-deref.cpp
@@ -331,3 +331,16 @@ void dangling_through_calls() {
   // expected-warning@-1 {{Use of 'local' after its lifetime ended}}
   // expected-note@-2    {{Use of 'local' after its lifetime ended}}
 }
+
+// If the same variable is dereferenced multiple times then only
+// report for the first dereference.
+void multiple_deref() {
+  int *ptr = nullptr;
+  {
+    int a = 5; // expected-note {{'a' initialized to 5}}
+    ptr = &a; // expected-note  {{Value assigned to 'ptr'}}
+  } // expected-note            {{'a' is destroyed here}}
+  *ptr = 6; // expected-note    {{Use of 'a' after its lifetime ended}}
+  // expected-warning@-1        {{Use of 'a' after its lifetime ended}}
+  *ptr = 7;
+}

>From 71cc3708e2858247327d0095384a7e503d427de7 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Mon, 10 Aug 2026 23:50:19 +0200
Subject: [PATCH 2/4] Update doc comment for markAsReported.

---
 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
index 8cfe4526d0d51..18f3014cfa6e9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
@@ -23,8 +23,8 @@ bool isBoundToLifetimeSource(ProgramStateRef State, SVal Val);
 /// descriptive name cannot be constructed for it.
 std::string getRegionName(const MemRegion *Reg);
 
-/// Returns true if \p R is seen the first time. If R was alreay reported
-/// before returns false.
+/// Returns the updated \p State with \p R marked as reported if \p R is seen
+/// the first time. Returns nullptr if \p R was already reported.
 ProgramStateRef markAsReported(ProgramStateRef State, const MemRegion *Region);
 
 } // namespace clang::ento::lifetime_modeling

>From 66a11b2b7cbd7b0db0e0724b7274bd9d9dbca6a8 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 11 Aug 2026 00:10:56 +0200
Subject: [PATCH 3/4] Clean up dead symbols.

---
 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index d4a3cfd060aa3..5151aa3ac3fe4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -173,6 +173,7 @@ void LifetimeModeling::checkDeadSymbols(SymbolReaper 
&SymReaper,
   ProgramStateRef State = C.getState();
   LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
   DeallocatedSourceSetTy Sources = State->get<DeallocatedSourceSet>();
+  ReportedDeadRegionsTy Reported = State->get<ReportedDeadRegions>();
 
   for (SVal Val : llvm::make_first_range(LBMap)) {
     if (const auto *R = Val.getAsRegion(); R && SymReaper.isLiveRegion(R))
@@ -189,6 +190,11 @@ void LifetimeModeling::checkDeadSymbols(SymbolReaper 
&SymReaper,
     if (!SymReaper.isLiveRegion(Region))
       State = State->remove<DeallocatedSourceSet>(Region);
   }
+
+  for (const MemRegion *Region : Reported) {
+    if (!SymReaper.isLiveRegion(Region))
+      State = State->remove<ReportedDeadRegions>(Region);
+  }
   C.addTransition(State);
 }
 

>From a44befc77019d7a6fa68bd071a0a9e0deb05d70d Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 11 Aug 2026 13:31:14 +0200
Subject: [PATCH 4/4] Include ReportedDeadRegions in printState.

---
 .../Checkers/LifetimeModeling.cpp             | 26 ++++++++++++-------
 clang/test/Analysis/dangling-ptr-deref.cpp    |  2 +-
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 5151aa3ac3fe4..7074e715d789b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -89,9 +89,10 @@ bool lifetime_modeling::isDeallocated(ProgramStateRef State,
 
 ProgramStateRef lifetime_modeling::markAsReported(ProgramStateRef State,
                                                   const MemRegion *Region) {
-  if (State->contains<ReportedDeadRegions>(Region->getBaseRegion()))
-    return nullptr;
-  return State->add<ReportedDeadRegions>(Region->getBaseRegion());
+  ProgramStateRef NewState =
+      State->add<ReportedDeadRegions>(Region->getBaseRegion());
+
+  return (NewState != State) ? NewState : nullptr;
 }
 
 static ProgramStateRef bindSource(ProgramStateRef State, SVal RetVal,
@@ -201,14 +202,21 @@ void LifetimeModeling::checkDeadSymbols(SymbolReaper 
&SymReaper,
 void LifetimeModeling::printState(raw_ostream &Out, ProgramStateRef State,
                                   const char *NL, const char *Sep) const {
   auto LBMap = State->get<LifetimeBoundMap>();
+  ReportedDeadRegionsTy Reported = State->get<ReportedDeadRegions>();
 
-  if (LBMap.isEmpty())
-    return;
+  if (!LBMap.isEmpty()) {
+    Out << Sep << "LifetimeBound bindings:" << NL;
+    for (auto &&[OriginSym, SourceSet] : LBMap) {
+      for (const auto *Region : SourceSet)
+        Out << " Origin " << OriginSym << " contains Loan " << Region << NL;
+    }
+  }
 
-  Out << Sep << "LifetimeBound bindings:" << NL;
-  for (auto &&[OriginSym, SourceSet] : LBMap) {
-    for (const auto *Region : SourceSet)
-      Out << " Origin " << OriginSym << " contains Loan " << Region << NL;
+  if (!Reported.isEmpty()) {
+    Out << Sep << "Reported regions: " << NL;
+    for (const auto *Region : Reported) {
+      Out << " " << Region << NL;
+    }
   }
 }
 
diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp 
b/clang/test/Analysis/dangling-ptr-deref.cpp
index 58f4aa7fa7583..55dd5eadc8ad0 100644
--- a/clang/test/Analysis/dangling-ptr-deref.cpp
+++ b/clang/test/Analysis/dangling-ptr-deref.cpp
@@ -342,5 +342,5 @@ void multiple_deref() {
   } // expected-note            {{'a' is destroyed here}}
   *ptr = 6; // expected-note    {{Use of 'a' after its lifetime ended}}
   // expected-warning@-1        {{Use of 'a' after its lifetime ended}}
-  *ptr = 7;
+  *ptr = 7; // no-warning: Already reported this base region.
 }

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

Reply via email to